Vue2入门学习笔记
Vue2入门必备!⭐关注我查看更多配套笔记
学习视频:https://www.bilibili.com/video/BV1Zy4y1K7SH/
【尚硅谷Vue全家桶】
本博客是对该视频内容的整理以及加入自己的理解 想全面学习的推荐大家去看原视频
1.Vue动画
自己使用 Css 也能实现
<!-- 使用标签transition包裹 -->
// apper属性可以让动画加载时出现 简写 apper 属性直接添加
<transition name="hello" :apper="true" apper>
<div>
<button @click="isShow = !isShow">
显示/隐藏
</button>
<h1 v-show="isShow">
我是动画元素
</h1>
</div>
</transition>
<!-- 必须使用 vue 规定的名称识别 -->
<style>
/* .之后的名称与name一致 不加name默认为v */
.hello-enter-active {
animation: 动画名 持续时间;
}
.hello-leave-active {
animation: 动画名 持续时间 everse;
}
.v-enter-active {
animation: 动画名 持续时间;
}
.v-leave-active {
animation: 动画名 持续时间 everse;
}
@keyfarm 动画名{
...
}
</style>
2.Vue过渡
<!-- 使用标签transition包裹 -->
// apper属性可以让动画加载时出现 简写 apper 属性直接添加
<transition name="hello" :apper="true" apper>
<div>
<button @click="isShow = !isShow">
显示/隐藏
</button>
<h1 v-show="isShow">
我是动画元素
</h1>
</div>
</transition>
<!-- 必须使用 vue 规定的名称识别 -->
<style>
/* .之后的名称与name一致 不加name默认为v */
/* 给所需动画添加过度属性 */
h1 {
transition: all 0.5s linear;
}
/* 初始效果 离开的终点 */
.v-enter,.v-leave-to {
animation: 动画名 持续时间;
}
/* 过渡效果中 */
.v-enter-active,.v-leave-active {
transition: all 0.5s linear;
}
/* 结束效果 结束的起点 */
.v-leave-enter-to,.v-leave {
animation: 动画名 持续时间 everse;
}
@keyfarm 动画名{
...
}
</style>
3.多个元素过渡
<!-- 使用标签transition包裹 -->
// apper属性可以让动画加载时出现 简写 apper 属性直接添加
<button @click="isShow = !isShow">
显示/隐藏
</button>
<!-- 多个动画添加需要使用transition-group -->
<transition-group group name="hello" apper>
<div>
<h1 v-show="isShow">
我是动画元素
</h1>
<h1 v-show="isShow">
我是动画元素
</h1>
</div>
</transition-group>
<!-- 必须使用 vue 规定的名称识别 -->
<style>
/* .之后的名称与name一致 不加name默认为v */
/* 给所需动画添加过度属性 */
h1 {
transition: all 0.5s linear;
}
/* 初始效果 离开的终点 */
.v-enter,.v-leave-to {
animation: 动画名 持续时间;
}
/* 过渡效果中 */
.v-enter-active,.v-leave-active {
transition: all 0.5s linear;
}
/* 结束效果 结束的起点 */
.v-leave-enter-to,.v-leave {
animation: 动画名 持续时间 everse;
}
@keyfarm 动画名{
...
}
</style>
4.超级好用的动画库
animate.css
<transition
enter-active-class="animate__fadeIn"
leave-active-class="animate__fadeOut"
>
<h1 v-show="visible" class="animate__animated">Animate.css</h1>
</transition>
// 使用方法
// name="animate__animated animate__bounce" 在 transition中添加该属性
// 在官网找到喜欢的属性配置 复制添加到
// enter-active-class="" leave-active-class=""
// npm安装
// npm install animate.css
// 引用 animate.css 动画库
import animate from 'animate.css'
Vue.use(animate)
官网:Animate.css | A cross-browser library of CSS animations.
5.Http请求 代理
// 对 xhr 进行封装
// xhr new XMLHttpRequest() xhr.open() xhr.send()
// jQuery $.get $.post
// axios
// 与 xhr平级
// fetch
Vue 项目中 推荐使用 axios
解决跨域问题
1.CORS
2.Jsonp script src 只能使用 get
3.代理服务器 代理服务器默认端口号 8080
1.-- nginx
2.-- vue-cli
5.1简易代理
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 在配置文件中添加以下配置 开启代理服务器 代理服务器开启在 8080
devServer: {
// 需要转发给谁
proxy:'http://后端服务器地址',
}
})
/*
注意
8080端口服务器 的根目录为 public 其中有的东西直接回返回
不会转发请求
*/
// 在axios中请求
axios.get('http://localhost:8080/后端路由地址').then(
response =>{
},
errror =>{
},
)
5.2完美代理
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 在配置文件中添加以下配置 开启代理服务器 代理服务器开启在 8080
devServer: {
proxy:{
// 配置 转发服务器的前缀
'/api':{
// 转发地址
target:'http://后端服务器地址',
//加入此配置项 转发地址为 /api/student 替换为 /student 不然会请求原路径
pathRewrite:{
'^/api':''
},
ws:true, // 用于支持 websocket
changeOrigin:true, // 说谎(伪装请求地址) 默认为 true
},
// 可配置多个
'/cas':{
target:'http://后端服务器地址',
pathRewrite:{
'^/cas':''
},
ws:true,
changeOrigin:true,
}
}
}
})
axios.get('http://localhost:8080/配置的前缀/后端路由地址')
6.静态组件
import '....' // vue 会进行严格的语法检查 放在assat进行引入
// 推荐放入 public 文件夹 在 index.html 中使用 link 引入 bootstrap.. 等
ES6 合并对象语法 Obj = {...对象1名称,...对象2名称}
7.vue-resource vue的插件库
对 xhr 进行封装
Vue.use(vueResource) 在main.js中进行全局配置
在 vm 中会出现 $http
使用 this.$http 代替 axios api完全一致 vue1.0广泛使用
8.slot 插槽
8.1默认插槽
<!-- 正常自闭和组件 / 正常组件 -->
<Student/>
<Student></Student>
<!-- 相同组件需要展示不同的元素时使用 -->
<!-- 默认插槽写法 -->
<!-- 父组件 -->
<Student>
<!-- 需要插入的内容 -->
<img src="">
</Student>
<!-- 子组件 -->
<div>
<!-- 父组件传入插槽的内容 -->
<slot>使用者没有传入插槽 会将我展示</slot>
</div>
<!-- style 可以在 父/子 组件书写 -->
8.2(具名插槽)多个插槽
<!-- 多个插槽写法 -->
<!-- 父组件 -->
<Student>
<!-- 需要插入的内容 -->
<img src="" slot="slot名称1">
<vido src="" slot="slot名称2">
<!-- template写法 template标签 不影响结构-->
<!-- slot:slot名称1 为 vue2.7 更新的写法 只能在 template标签中使用 -->
<template slot="slot名称1" slot:slot名称1>
<h1>我是h1</h1>
<div>我是div</viv>
</template>
</Student>
<!-- 子组件 -->
<div>
<!-- 父组件传入插槽的内容 -->
<slot name="slot名称1">使用者没有传入插槽 会将我展示</slot>
<slot name="slot名称2">使用者没有传入插槽 会将我展示</slot>
</div>
<!-- style 可以在 父/子 组件书写 -->
8.3作用域插槽
<!-- 作用域插槽写法 -->
<!-- 父组件 -->
<Student>
<!-- 作用域必须使用 template 标签 传参 作用域 -->
<template scop="Obj">
<!-- games msg 在Obj中 -->
<h1>{{Obj.games}}</h1>
<h1>{{Obj.msg}}</h1>
</template>
</Student>
<!-- 子组件 -->
<div>
<!-- 子组件插槽中给父组件传参 -->
<slot :games="games" msg="这里是msg信息">使用者没有传入插槽 会将我展示</slot>
</div>
<!-- style 可以在 父/子 组件书写 -->
9.Vuex
多组件共享数据 状态 = 数据
- 多个组件依赖于同一个状态
- 来自不同组件的行为需要修改同一状态
1.npm i vuex
2.import Vuex from 'vuex' // 在 index.js 中 引入
// vue2 -> vuex3
// vue3 -> vuex4
3.Vue.use(Vuex)
Vue({
store:"hello",
})
// 在 vm 中 出现 $store 保存传入的数据
在components同级文件夹下添加 store 文件夹
添加 index.js
// 该文件用于创建 Vuex 中最为核心的store
// 引入
import Vue from 'vue'
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)
// 准备 actions
const actions = {
// 需要dispatch的函数
"函数名"(context,value){
console.log('actions 中的函数被调用了',context,value);
// 分配给 其他 dispatch 分段处理 便于维护代码
context.dispatch("函数2",value);
context.commit("函数名(使用大写与actions进行区分)",value);
}
}
// 准备 mutations 操作 data
// 开发者工具 检测 mutations
const mutations = {
"大写函数名"(state,value){
state.value += value;
}
}
// 准备 state 用于存储数据 全局 data
const state = {
// 准备数据
"value":0,
// 改变后会替代 vc 里的 data
}
// 实现全局的 计算属性 computed
const getters = {
// 对 state 进行加工
bigSum(state){
return state.sum*10;
}
}
// 创建并暴露 store
export default new Vuex.Store({
actions,
mutations,
state,
getters,
})
main.js
import store from './store/index.js'
// 配置项中添加store
new Vue({
store
})
js 执行 import 全部 import 会被提升到最高层
vue 规定必须 先 ues(vuex) 再 创建 store 实例
使用2.import Vuex from 'vuex'
// vue2 -> vuex3
// vue3 -> vuex4
3.Vue.use(Vuex)
methds:{
this.$store.dispatch("事件名",传入的数据);
// 直接跨过action
this.$store.commit("事件名",传入的数据);
// 获得计算属性
this.$store.getters.bigSum;
}
可直接夸过 Actions 进行 Commit 调用 mutation 的函数
vuex 开发者工具 历史记录符号
回到该次数据 历史记录符号
可以删除历史记录 禁用符号
合并记录 下载符号
底部分 可以 导入导出 / 粘贴复制
ES6 语法
let Obj1 = {x:1,y:2}
let Obj2 ={
z:3,
...Obj1, // 直接将Obj1的键值对插入
m:7,
}
9.1 mapState mapGetters
import {mapState} from 'vue'
// 映射 State 中的数据
computed:{
// 借助mapState生成计算属性 从state中读取数据
...mapState({sum:'sum',adress:'adress'}) // 可直接在计算属性中添加
// 不可简写 对象简写方式为 {sum:sum} === {sum} !== {sum:"sum"}
// Vue 可识别 简写方法 与上述方法用途一致
...mapState(['sum','subject'])
// 全局计算属性获取
...mapGeters(['bigSum'])
}
9.2 mapAction mapMutations
import {mapState} from 'vue'
// 映射 State 中的数据
methods:{
// 数组 / 对象 形式默认生成方法 需要在调用时传参
/*add(value){
this.$store.commit('add',value)
}*/
...mapActions(['add']),
...mapMutations(['add'])
}
9.3Vuex模块化
index.js
import Vuex from 'vuex'
// 计数相关属性
const countOptions = {
// 配置该项可在 组件 中使用更简洁的写法
namespaced:true,
// 与正常 vuex 配置完全一致
action:{},
mutation:{},
state:{},
getter:{},
}
// 人员相关属性
const personOptions = {
action:{},
mutation:{},
state:{},
getter:{},
}
export default new Vuex.Store({
modules:{
countAbout:countOptions,
personAbout:personOptions
}
})
// 组件中简写使用
computed:{
// 普通简写
// 调用 state时 需要使用 countAbout.data
...mapState(['countAbout','personAbout']),
// 需要在 vuex 中 开启 namespaced
// 调用时 直接使用 data 就可以
...mapState('countAbout',['data']),
...mapState('personAbout',['data']),
// 其他 map 同理使用
// getter 模块化之后 值为 countAbout/bigSum countAbout不再为对象
}
10.路由
10.1 路由基础
单页面应用 路由改变 页面局部刷新 (没有全部刷新)
使用 vue 的插件库 vue-router
npm install -g vue-router
/*
vue-router 3 版本对应 vue 2
vue-router 4 版本对应 vue 3
*/
// npm install vue-router@3
在 components 同级文件夹下创建 router 文件夹
index.js
// 引入 vue-router
import VueRouter from 'vue-router'
// 引入组件
import TheList from '../components/TheList.vue';
import AboutSnowman from '../components/AboutSnowman.vue';
// 创建 并 暴露 一个路由器
export default new VueRouter({
routes: [
{
path:"/TodoList",
component:TheList,
},
{
path:"/about",
component:AboutSnowman,
},
// path 为 * 表示匹配全部 可以匹配全部路径 路径匹配为从上至下
{
path:"*",
component:AboutSnowman,
},
]
})
main.js
// 引入 路由器
import router from './router'
new Vue({
render: h => h(App),
// 设置 router
router:router,
}).$mount('#app')
<!-- 最终 在 html 页面中转化为 a 标签 -->
<!-- to 为跳转到的路由 -->
<!-- active-class 为在该路由时应用的 样式 -->
<router-link to="/TodoList" active-class="active">每日清单</router-link>
<router-link to="/about" active-class="active">关于我的主页</router-link>
<!-- 视图展示 对应路由的位置 -->
<router-view></router-view>
在 components 同级文件夹下创建 pages 文件夹
便于分类 pages存放 路由组件 components文件夹存放 一般组件
原理 : {不断销毁与挂在组件}
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个router, 可以通过组件的$router属性获取到
10.2 多级路由 (嵌套路由)
index.js
// 引入 vue-router
import VueRouter from 'vue-router'
// 使用插件
import Vue from 'vue'
Vue.use(VueRouter)
// 引入组件
import TheList from '../components/TheList.vue';
import AboutSnowman from '../components/AboutSnowman.vue';
// 创建 并 暴露 一个路由器
export default new VueRouter({
routes: [
// 一级路由
{
path:"/TodoList",
component:TheList,
// 路由嵌套从这里开始
// 二级路由
children:[{
// 第二级不需要加 / 会出问题
path:"new",
component:TheList,
}]
},
// 一级路由
{
path:"/about",
component:AboutSnowman,
},
]
})
<!-- 最终 在 html 页面中转化为 a 标签 -->
<!-- to 为跳转到的路由 -->
<!-- active-class 为在该路由时应用的 样式 -->
<!-- 二级路由必须携带父亲级路由的路径 -->
<router-link to="/TodoList/new" active-class="active">每日清单</router-link>
<router-link to="/about/new" active-class="active">关于我的主页</router-link>
<!-- 视图展示 对应路由的位置 -->
<router-view></router-view>
10.3 路由传参
query参数 get传参
<router-link to="/TodoList/new?name=雪人&age=18" active-class="active">每日清单</router-link>
<!-- 加 v-bind + ES6 `` 动态传参 -->
<router-link :to="`/TodoList/new?name=${name}&age=${age}`" active-class="active">每日清单</router-link>
<!-- 对象式 传参 -->
<router-link :to="`{
path="/TodoList/new",
query:{
name:name,
age:age,
}
}
`" active-class="active">每日清单</router-link>
<router-link to="/about/new" active-class="active">关于我的主页</router-link>
// 组件中获取
this.$route.query.name
this.$route.query.age
10.4 命名路由
export default new VueRouter({
routes: [
{
// 配置姓名 简化传参
name:'ListName',
path:"/TodoList",
component:TheList,
children:[{
// 配置姓名 简化传参
name:'new',
path:"new",
component:TheList,
}]
},
]
})
<!-- 对象式 传参 -->
<!-- 使用 name 必须使用对象式 -->
<router-link :to="`{
// 此处直接传入 路由器 定义的名字
name:"ListName",
query:{
name:name,
age:age,
}
}
`" active-class="active">每日清单</router-link>
10.5 params参数
<!-- params参数 传参 需要配置占位符 -->
<router-link :to="`/TodoList/new/11/王二`" active-class="active">每日清单</router-link>
<!-- params参数 对象式 -->
<router-link :to="`{
// params 只能使用name path会报错!!!
name:"ListName",
params:{
id:11,
name:"王二",
}
}
`" active-class="active">每日清单</router-link>
export default new VueRouter({
routes: [
{
// 添加占位符设置参数
name:'ListName',
path:"/TodoList/:id/:name",
component:TheList,
children:[{
// 添加占位符设置参数
name:'new/:id/:name',
path:"new",
component:TheList,
}]
},
]
})
// 组件中获取
this.$route.params.name
this.$route.params.age
10.6 props的配置
export default new VueRouter({
routes: [
{
name:'ListName',
path:"/TodoList/:id/:name",
component:TheList,
children:[{
name:'new/:id/:name',
path:"new",
component:TheList,
// props 写法一
// 该对象中的所有 key-value 都会以props的形式传给 组件 TheList中
props:{id:1,name:'hello'},
// props 写法二
// props值为布尔值,若布尔值为真,就会把该参数的 params值当做 props 值传递给组件
props:true,
// props 写法三
// 值为函数 返回值 以props 传递给组件
// $route 被回调传入 真好
props($route){
return {id:$route.query.id,name:'hello'}
}
// 结构赋值写法
props({query}){
return {id:query.id,name:query.name}
}
}]
},
]
})
10.7 replace 记录
默认为push记录 栈的不断添加
replace 替换当前记录 不能返回
<!-- 开启replace :replace="true" -->
<router-link to="/TodoList/new" active-class="active" :replace="true">每日清单</router-link>
<!-- 开启replace 简写方式 -->
<router-link to="/about/new" active-class="active" replace>关于我的主页</router-link>
10.8 编程式路由导航
在 $router 中 直接操作 不借助 router-link
<template>
<button @click="showPush">
push查看
</button>
<button @click="showReplace">
replace查看
</button>
<button @click="back">
后退
</button>
<button @click="forward">
前进
</button>
<button @click="go">
前进
</button>
</template>
<script>
export default {
name:'Message',
methods:{
showPush(){
// 调用 $router 上的API
this.$router.push({
name:"xiangqing",
query:{
id:'11',
name:'王二',
}
})
},
showReplace(){
// 调用 $router 上的API
this.$router.replace({
name:"xiangqing",
query:{
id:'11',
name:'王二',
}
})
},
back(){
this.$router.back
},
forward(){
this.$router.forward
},
go(){
// 正数 前进 n 次
// 复数 后退 n 次
this.$router.go(n)
},
}
}
</script>
10.8 缓存路由组件
组件切走时会被销毁 内容消失 如何保存?
需要保存数据的去缓存
<!-- inlude="" 配置需要缓存的组件 否则缓存全部组件 -->
<!-- 防止组件被销毁 数据丢失 -->
<keep-alive inlude="组件名">
<router-view></router-view>
</keep-alive>
<!-- 缓存多个组件 -->
<keep-alive :inlude="['组件名1','组件名2','组件名3']">
<router-view></router-view>
</keep-alive>
10.9激活与失活声明周期
用于路由组件的 开启定时器 比 mounted更高效
methods:{
activated(){
// 开启定时器
},
deactivated(){
// 定时器自动关闭
}
}
10.10路由守卫
1.前 / 后 置路由守卫
保护路由的安全 验证数据库再展示
全局前置路由守卫
export default new VueRouter({
routes: [
{
path:"/TodoList",
component:TheList,
// 用于存放用户需要的特殊数据
// 可以在判断中进行索引 不指定默认为 undefined 为 false
meta:{isAuth:true,title:"每日清单"},
}
]
})
// 全局前置路由守卫
// to 路由去向 路由对象
// from 路由由来 路由对象
// next 回调函数 使用 next()才能切换路由
router.beforeEach((to,from,next)=>{
if(to.path === '/home'){
// 利用meta判断
if(from.meta.isAuth){
next()
}
next()
}
next()
})
export default router
// 全局后置路由守卫
// 无 next 路由跳转已经完成后触发
router.afterEach((to,from)=>{
// 可在跳转完成后更改路由
doucment.title = to.meta.title || '网页标题' // 短路判断
})
2.独享路由守卫
export default new VueRouter({
routes: [
{
path:"/TodoList",
component:TheList,
// 用于存放用户需要的特殊数据
// 可以在判断中进行索引 不指定默认为 undefined 为 false
meta:{isAuth:true,title:"每日清单"},
// 独享路由守卫 仅有前置 无后置
beforeEnter:(to,from,next)=>{
if(...){
next()
}
}
}
]
})
3.组件内路由守卫
<script>
mounted(){
// 通过路由器规则 进该组件时被调用
// 在beforeEnter 之前
// to 一定是该组件路由
beforeRouteEnter(to,from,next){
},
// 通过路由器规则 离开该组件时被调用
// from 一定是该组件路由
beforeRouteLeave(to,from,next){
}
}
</script>
11.浏览器路由模式
main.js
// hash 带 # 兼容性好 不美观
// history 不带 # 兼容性差(相对) 美观 会寻找服务器路径资源 容易出错(后端解决)
new VueRouter({
// 设置模式
mode:history,
})
12.打包 上传服务器
>>> npm run build
// 生成 dist 文件夹
// 为打包后的 css js html 文件
// 直接点击 index.html 无法运行必须在服务器部署
如何部署服务器
node.js express 框架
->npm init
->text_serve 包名
->npm i express
中间件解决 history 模式 404问题
->npm i connect-history-api-fallback
// 在 server.js 同级目录下 新建 js
const express = require('express')
const history = require('connect-history-api-fallback')
const app = express()
// 解决history 404 问题
app.use(history)
// 默认选择 index.html
app.use(express.static(__dirname+"./static"))
app.get('/person',(req,res)=>{
res.send({
name:'tom',
age:18
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动成功了!')
})
13.Vue UI 组件库
7.1 移动端常用组件库
- Cube UI https://didi.github.io/cube-ui
- Mint Ul http://mint-ui.github.io
7.2PC端常用UI组件库
- Element Ul https://element.eleme.cn
- lView Ul https://www.iviewui.com
- ant design Ant Design - 一套企业级 UI 设计语言和 React 组件库
直接用就行
此处介绍Element UI
->npm i element-ui
import Vue from 'vue';
import ElementUI from 'element-ui';
// 引入全部样式
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
// 此处use 后 会注册全部组件为全局组件
// 引入的 js 非常大 7.2M(别用)
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
按需引入
-> npm install babel-plugin-component -D 按需引入的库 -D为开发依赖(开发环境)
->npm install babel-preset-es2015 --save
->npm install --save-dev @babel/cli
需要修改 babel.config.js 配置
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
['es2015', { modules: false }],
],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk',
},
],
],
}
main.js
// 按需引入
import {Button,Row,...} from 'element-ui'
// 配置之后会引入需要的 css 而不是全部css
import 'element-ui/lib/theme-chalk/index.css';
// Button 的组件名 可以自定义
// 注册全局组件
VUe.component(Button.name,Button)