vue 2 and 3
安装与使用
1.用 script 引入
1.创建一个Vue实例 let v = new Vue({el:"#app",data:{name:"zs"}})
1.如果el省略 可以随后再挂载 setTimeout(()=>{v.$mount("#app")},1000) 1s 后显示
2.在 <div id+"app">{{name}} app容器中引入 数据
3.app 容器被称为 Vue 模板 该模板符合html规范 只不过混入了特殊的 Vue语句 {{}} 里面是 js 表达式 a 1+1 Date.now()
2.用 npm 安装脚手架
基本知识
vue的一些特点
1.想用Vue 必须创建实例 并且为Vue实例提供 Vue模板 在Vue模板中仍然符合Html规范 只是多了一些 Vue代码
2.Vue实例和模板一一对应 当然可以 2v2 但真实的开发过程中 都是 1v1 配合组件
3.{{xx}} xx 是js表达式 可以读取到 data任意属性
1.js表达式 与语句 除了 if for 这些是语句 其他大部分是 表达式 可以用js的任意函数
插值语法和指令语法
4.模板里面的引用 包括 插值语法 和 指令语法
1.插值 {{}}
1.methods 中的方法 @click = "show/show()" 都可以 但在插值语法中 必须加 () 否则显示函数本身
2.指令
1.v-bind:href="xxx" xxx 为data里面的属性 当用属性的时候 必须 v-bind 否则 按原始解析 简写为:
1.注意 xxx 也是js里面的表达式 只有写: 才是js表达式 否则原始输出
2.<div class="normal" :class="cName"> 可以同时操作class 加不加:号 不覆盖
3.:calss :style 可以操作元素 css
1.:class
1.字符串写法 :class=cName 用于类名不确定 需要动态获取
data:{cName:"abc"}
2.对象写法 用于 要绑定多个样式 个数不确定 名字不确定
data:{cNameObj:{c1:false,c2:true}}
3.数组写法 用于 要绑定多个样式 个数确定 名字确定 不确定用不用
data:{cNameArr:["c1","c2"]}
2.:style = stleObj
1.对象写法 data:{stleObj:{fontSize:"25px",color:"red"}}
2.数组写法 data:{stleArr:[{fontSize:"25px",color:"red"},{border:"1px solid red"}}
2.v-model:value 双向绑定 简写为 v-model 因为 v-model 默认收集的就是 value数值
1.只能用在表单输入类元素 像 <h1 v-model:x>abc</h1> 绑定会失败
2.v-model 的三个修饰符
1.v-model.number lazy trim
3.v-on:click="show(6)" v-on:click="show" 传参有括号 不传参没有 简写 @
0.click="show" click = "sortType = 0" 都可以 函数 或者 js 表达式 都可以 作为 事件的数值
1.没有参数 第一个参数默认是 event 有参数如果想用 event 怎么半 v-on:click="show($event,666)"
2.用 $event 占位后 去定义的方法中就可以接到
2.事件修饰符
1.prevent 阻止默认 e.preventDefault
2.stop 阻止冒泡 e.stopPropagation()
3.once 执行一次
4.capture 捕获阶段执行 [默认冒泡 过程: 从外向内[捕获] 从内向外[冒泡] ]
5.self 不是自己不执行 变相阻止冒泡
6.passive 事件的默认行为立即执行 不用等待回调函数执行完毕 鼠标滚轮 wheel 事件
7.@click.stop.prevent = "show" 可以连写 @kedown.ctrl.y 按ctrl + y 触发
3.键盘事件
1.e.key 键名 e.keyCode 键代码
2.按键事件 修饰符
1.enter delete esc space tab[必须配合keydown 因为焦点跑了] up down left right [常见的9个]
2.对于不常见的 可以用原始的 e.key 这个数值去绑定他们 单要注意 CapsLock 需要转为 caps-lock 形式
3.系统修饰键 用法比较特殊 ctrl shift alt meta[win]
1.配合 keyup使用的时候 按下特殊键 按下其他键 再释放其他键 才能触发
2.配合 keydown 使用正常
4.也可以用keycode 配合 比如 keyup.13 也能触发 但不推荐
5.也可以自己定义修饰符 Vue.config.keyCodes.huiche = 13 @keydown.huiche = "show"
4.<div v-show="false"> <div v-if="false">
1.v-show 结构在 只是不显示[display:none] v-if 直接从 dom节点删除了
2.v-if v-else-if v-else
3.<div v-if="false">1</div> <div v-else-if="false">2</div> <div v-else>3</div>
1.v-else 就不用写条件了 和 js
4.<template class="out" v-if="true">aaaabbbb</template> 不影响结构 如果 .out 是div就多了一层结构
1.template 不会被解析到 dom template 无法和 v-show 配合
5.<li v-for=”(item,index) in persons“>p</li>
1.遍历对象 遍历数组 遍历字符串 遍历次数 :key 有 效率高 有利于虚拟dom的对比算法 如果没有写 :key 默认用遍历索引
1.这个:key在真实dom中是没有的 只是在虚拟dom中用
2.如果用 index 作为key 不能破坏顺序
6.v-text
1.<div v-text="msg"></div> msg 会替换掉 div中间的内容 如果msg中有html标签 不会解析 原样输出
7.v-html
1.能够解析 html标签
8.v-cloak [防止css资源加载过慢 满屏的 {{}}]
1. 没有数值 配合css隐藏元素 [v-cloak]{display:none}
2.Vue 开始解析后 忽略所有 v-cloak 属性 于是 css 不在生效 就显示出来了
8.v-once 一次渲染 不在解析 data 数据变化 不影响 它
9.v-pre 不解析 如果有插值语法 直接显示 为了不需要解析的行加 高效率
1.可以跳过没有插值语法 的 html 元素结构 不用解析分析 提高解析效率
10.自定义指令 <h4 v-capital="name"></h4> //把 name 的值 首字母大写 作为 h4 的 innerText
1.执行时机 1.指令与元素成功绑定时 2.指令所在的模板被解析时 简单代码如下
directives:{
capital(element,bounding){
element.innerText = bounding.value.substring(0,1).toUpperCase() + bounding.value.substring(1)
}
}
2.完整写法
capital:{
//指令与元素绑定时候执行
bind(element,binding){
element.value = binding.value.substring(0,1).toUpperCase() + binding.value.substring(1)
},
//指令所在元素插入模板瞬间
inserted(element,binding){
//焦点之 和 取父元素 在这里合理
element.focus()
}
},
//指令所在的模板被重新解析
update(element,binding){
element.value = binding.value.substring(0,1).toUpperCase() + binding.value.substring(1)
element.focus()
}
}
3.指令函数中的 this 都是 window 指令定义时候 不加 v 用的时候 加 v
4.全局指令 Vue.directive("copital",{bind(),inserted(),update()})
5.指令名如果是多个单词 推荐 用 kebab-case 不推荐 camelcase
11.ref 指令 类似 id <div ref="title"></div> 在 vc vm 实例上 this.refs.title 可以拿到原始元素
1.对于元素来说 id 和 ref 差不多 单对于 子组件来说 ref 可以 引用到 当前使用的子组件的 vc 实例对象 但是如果此时给
2.子组件加 id 属性 拿到的却是 子组件解析后的 html 元素 <School ref="sch"/> this.refs.sch
组件和子组件
1.props 父组件给子组件传属性 打款 <Student name=="zs" age=18 sex="女"/>
1.子组件需要确认收款 props:["name","age","sex"]
2.如果年纪传过去不要字符串 要number 怎么办 <Student name=="zs" :age=18 sex="女"/> 给它 v-bind 一下 会按照 js 表达式解析
3.于是 类似于 let a =18 就变数值型了
4.如果想约束类型怎么办
1.需要子组件进行类型限制 props:{name:{type:String,required:true;default:"zs"}}
5.传进来的数据 都放在 vc 实例上
6.如果传进来的属性 和 data:{return ()=>{}} 里面的属性冲突[data 有 name 又传进来 个 name] 怎么办
1.以传进来的为准 来的都是客 [vc 先读取 props 里面的属性 再去读取 data 里面的属性 读到了 页面上需要 直接渲染]
2.所以可以改 data里面的属性 赋值 传进来的属性
7.传进来的属性是不允许需改的 如果想修改怎么办
1.可利用 vc 先处理 props 里面的属性数据 当处理 data里面的数据时候 vc 身上已经挂着 props 里面的数据 于是再 data里面
2.可以这么写 data:function(){return {myname:this.name}} 然后去修改 myname 变相的修改了name
8.props 接受 属性的三种方式
1.props:["name","age"]
2.props:{name:String}
3.props:{name:{type:String,default:"zs"}}
1.如果默认值是对象/数组 也要像 组件的 data 那样 写成函数 返回这个对象
1.props:{name:{type:String,default(){return {name:"zs"}}}}
9.其实传过来的值不接受 也是也可以看到的 在 this.$attrs 这个对象里面
1.如果传两个值 props 只接受了一个 那么其中一个在 this.$attrs,属于捡漏专业户
2.类似的还有 ths.$slots 但这里是全部报错 不管你有没有 slot 去接受
10.props 还可以于默认值
props:{
title:String,
date:{
type:String,
default:"2023-02-02 Def"
}
}
2.mixin 混合 [多个组件共用的配置 混入一个对象里面]
1.定义混合 export let hunhe = {data(){},methods:{}}
2.导入 inport {hunhe} from "hunhe.js"
3.引用
1.局部引入 data(){return{mixins:[hunhe,hunhe2]}}
2.全局引入 一般再 main.js 里面 Vue.mixin(hunhe)
4.冲突怎么办
1.如果自己组件中定义的 data methods 和 混合中的冲突 依 自己的为准 自己没有 混合有的 合并 总之 以自己为准合并对象
1.如果 mounted 等生命周期函数 冲突 两者都执行 不合并 不覆盖
3.插件 用于增强 Vue 功能
1.export default{install(Vue,a,b,c){Vue.filter("mySlice"){};Vue.directive("fbind"){};Vue.minin(hunhe)};Vue.prototype.}
2.使用插件 Vue.use(plugin) 于是 所有的 vm vc 都可以使用这些了
4.自定义事件 [通信] props 也是 [通信]
1.一种 父组件 与 子组件 之间的 通信方式
2.使用场景 子 想给 父 传数据 那就要在 父 中给 子 绑定自定义事件 事件回调 留在 父 中
3.绑定自定义事件的方法
1.在 父 中 <son @demo="fatherMethod"/> <son v-on:demo="fatherMethod"/>
1.子在自己的事件[比如click] 中 用 this.$emit("demo",paraA,paraB) 触发 dema 此时 父 回调 fatherMethod 收到参数
2.在 父 中 <son ref="myson"/>
1.mounted(){ this.$myson.son.$on/$once("demo",this.fatherMethod)}
1.这里注意 如果不用 父 mehod 中的 this.fatehrMethod 而是自己写 写 function
2.mounted(){ this.$myson.son.$on/$once("demo",function(){this})} 这里的 this 指向 子 操作父数据麻烦
1.可以用箭头函数
3.解绑自定义事件
1.子关闭 this.$off("demo1","demo2") 如果要关闭全部事件 this.$off()
4.在 父 中 可以给 子 组件上也可以绑定原生的 click 事件 需要加 @click.native="fatherMethod"
1.如果不加 统统被作为 自定义事件 需要子 this.$emit()
5.全局事件总线 [通信]
1.全局事件总线的安装
1.傀儡上线 每个组件都可以看到 都可以给它绑事件 都可以触发
new Vue({
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this;
}
}).$mount('#app')
2.使用事件总线
methods:{
demo(){
}
}
mounted(){
this.$bus.$on("demoHello",this.demo)
}
3.当组件销毁在 beforeDestroy 中 this.$bus.$off("demo")
6.消息订阅与发布 [通信]
1.使用步骤
1. 安装库 npm i pubsub-js
3.使用
1.A想接受消息 A订阅
1.引入库 import pubsub from "pubsub-js
2.mouted(){this.pubid = pubsub.subscribe("hello"),this.methodDemo}
1.methodDemo 参数 1 订阅名字 ”hello" 2 数据data
3.最好在 beforeDestroy 中取消订阅
1.pubsub.unsubscribe(this.pubid)
2.B 想发消息
1.引入库 import pubsub from "pubsub-js
2.pubsub.publish("hello",data)
7.改变一个元素后 让它立即获取焦点
1.好像之间 el.focus() 不行 需要调用 this.nextTick(fn) 在下一个渲染轮回渲染
this.editValue = todo.title
this.$nextTick(function(){
this.$refs["edit-input"].focus()
})
8.过渡与动画
1.进入的样式
1.v-enter 进入出发点
2.v-enter-to 进入目的地
3.v-enter-active 进入 出发点 到目的地 过程中 该以怎样的速度 样式表演 一般 transition:all 1s linear
2.离开样式
1.v-leave 离开 出发点
2.v-leave-to 离开 目的地
3.v-leave-active 离开 出发点 到目的地 过程中 该以怎样的速度 样式表演 一般 transition:all 1s linear
//css
.item-enter,.item-leave-to{
transform: translateX(600px);
opacity: 0;
}
.item-enter-to,.item-leave{
transform: translateX(0);
opacity: 1;
}
.item-enter-active,.item-leave-active{
transition: all 1s linear;
}
//html
<transition-group name="item" appear>
<MyItem v-for="todo in todos" :todo="todo" :key="todo.id"></MyItem>
</transition-group>
9.插槽 [通信]
1.默认插槽
子 Goods
<div class="out">
<slot></slot>
</div>
父 ul 结果会 默认进入 子的 slot 插槽中
<Goods>
<ul>
<li>1</li>
</ul>
</Goods>
2.具名插槽 [具有名字的插槽]
子 Goods
<div class="out">
<slot name="goods"></slot>
</div>
父
<template slot="goods">
<li>1</li>
<li>1</li>
</template>
<Goods>
<template v-slot:goods>
<li>1</li>
</template>
</Goods>
1.对于具名 来说 如果是 非 template 标签 不可以用 v-slot:slotName 只能用 slot="name"
1.对于 template 来说 可以用 slot="name" 也可以用 v-slot:slotName
2.但是 用 template 可以省一层标签
2.v-slot 和 slot 不可以加到 <Goods></Goods> 组件本身上面
3.作用域插槽
子 Goods
<template>
<div class="out">
<slot name="goods" :abc="goods"></slot>
</div>
</template>
<script>
export default {
name:"MyGoods",
data(){
return {
goods:["手表","手机"]
}
}
}
父
<Goods>
<template scope/slot-scope ="data" slot="goods">
<!-- {"abc": ["手表","手机"]} -->
<span>{{data}}</span>
<li v-for="item in data.abc" :key=item> {{item}} </li>
</template>
</Goods>
1.作用域插槽 数据 在子组件中 通过v-bind:abc 绑定 数据 [作用域 配合 具名 有名字 不用 无法插入插槽]
2.在父组件中 用 scope = "data" 引用出一个对象 比如 子 :abc data 数据 如 {"abc": ["手表","手机"]} 是一个对象
3.作用域插槽 父 必须包裹 template 否则会报错
1.<template scope="data" 或者 slot-scope="data">
4.父给的结构 将 保存在 this.$slots
1.类似的还有 this.$attrs [props 如果不接受]
vuex [通信] Vuex [v3.6.2 3 版本最新的 vuex 3 配合 vue 2 / vuex 4 配合 vue 3]
1.如何配置 vm vc 的 store
1.在 src 下 创建 store 并在其下创建默认的 index.js 代码如下
//注意 引入的 vue vuex 包 都是小写
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
//准备actions响应用户动作
const actions = {}
//准备 mutations修改 state 数据
const mutations = {}
//准备 state 保存具体数据
const state = {}
//创建并暴露store 注意这里的 new 中的 Store 是大写
export default new Vuex.Store({
actions,
mutations,
state,
})
2.模块化 不同的 store Count 和 person 为 完整的 store 配置文件
sotre 文件夹下 index.js
import Vue from "vue"
import Vuex from "vuex"
// 模块化 引入 Store 对象 Count
import Count from "./Count"
import Person from "./Person"
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
Count,
Person
}
})
//Count.js 代码如下 Person.js 也类似
export default {
// namespaced:true,
actions: {
/**
在这里
this 是 stroe
context 是一个 有 dispatch commit state 的包裹对象 之所有有 dispatch 可以调用 actions 中 方法
为 actions 中的复杂逻辑做 准备 可能多个 dispatch 调用 这里面的方法 只能通过 dispatch 调用
没办法 通过 this 调用 自己对象的方法 因为这里 this 的指向 store 这里的方法 分别存放在
this._actions 和 this._mutations 通过 dispatch commit vuex 自己会找对应的方法
*/
increaseOdd(context, value) {
//这里想方法 state context.state.sum
if (context.state.sum % 2) {
context.commit("Increase",value)
}
},
increaseWait(context, value) {
setTimeout(() => {
context.commit("Increase",value)
}, 500)
}
},
mutations: {
// 在这里
// this 是 stroe
// context 是一个 只能看到 state 的包裹对象
//这里想方法 state context.sum
Increase(context, value) {
context.sum += value;
}
},
state: {
sum: 0
},
getters: {
// bigSum(context){
// return context.sum * 10;
// }
}
}
2.如何访问 vuex [$store]
1.可以自己写方法 也可以用 mapActions,mapMutations,mapGetters 批量生成 在没有开启 namespaced:true 的情况下
<script>
import {mapActions,mapMutations,mapGetters} from "vuex"
export default {
name: "MyCount",
data() {
return {
n: 1,
}
},
computed:{
sum(){
return this.$store.state.Count.sum;
},
//...mapGetters({bigSum:"bigSum"}), 下面是自己写的方法
bigSum(){
return this.$store.getters.bigSum;
}
},
methods: {
...mapActions({increaseOdd:"increaseOdd",increaseWait:"increaseWait"}),
...mapMutations({Increase:"Increase"}),
//自己写的方法
// increaseOdd() {
// this.$store.commit("Increase",this.n)
// },
// handleAddOdd() {
// this.$store.dispatch("increaseOdd",this.n)
// },
// increaseWait() {
// this.$store.dispatch("increaseWait",this.n)
// }
},
mounted(){
console.log(this)
}
};
</script>
3.如果不知道如何访问 去 查 $store 属性 里面的 _actions _mutataions 对应方法 dispatch commit
4.如果导入了多个 sotore 配置文件
1.需要开启 namespaced = true; 开启命名空间后 store 配置文件 里面的内容 不需要发生任何改变 但是 访问它的 组件却要忙了
1.没有开启前 this.$store.dispatch("increaseOdd",this.n)
2.开启后 this.$store.dispatch("Count/increaseOdd",this.n) Count 就是它的命名空间
3.命名空间名字 哪里定的
1.sotre index.js 创建 store 对象的时候 new Vuex.Store(modules:{namespace:Count})
2.模式是 new Vuex.Store(modules:{Count:Count}) 省略为 new Vuex.Store(modules:{Count})
2.state 不管是否开启命名空间 都处于 命名空间访问状态 都是这一种形式
1.this.$store.state.Count.sum
3.但是 actions mutations setters 都区分是否开启了 命名空间
1.没有开启
1.getters: return this.$store.getters.bigSum
1. ...mapGetters({bigSum:"bigSum"})/...mapGetters(["bigSum"]),
2.actions:this.$store.dispatch("increaseOdd",this.n)
1. ...mapActions(["increaseOdd","increaseWait"])
1....mapActions({increaseOdd:"increaseOdd",increaseWait:"increaseWait"}) 对象模式
3.mutations:this.$store.commit("Increase",this.n)
1. ...mapMutations(["Increase"])
1....mapMutations({Increase:"Increase"}) 对象
2.开启后
1.getters:
//对象模式
...mapGetters("Count",{bigSum:"bigSum"}),
//数组模式
...mapGetters("Count",["bigSum"]),
2.actions:
...mapActions("Count",{increaseOdd:"increaseOdd",increaseWait:"increaseWait"}) 对象
...mapActions("Count",["increaseOdd","increaseWait"]), 数组
3.mutations:
...mapMutations("Count",{Increase:"Increase"}) 对象
...mapMutations("Count",["Increase"]) 数组
4.state:
...mapState("Count",["sum"]) // 数组
...mapState("Count",{sum:"sum"}) // 数组
5.对于 actions mutations 都是在 methods 里面
1.对于 state 和 getters 在 计算属性里面
vue-router [vue-router 4 配合 vue 3 | vue-router 3 配合 vue 2]
1路由的概念
1.一个路由就是一个 key value key 为路径 value 为 函数 或者 组件
2.路由的分类
1.后端路由
1.value 是 funtion 用户处理客户的请求 响应数据
2.前端路由
1.value 是 components 显示页面内容 路径改变 显示不同组件
2.安装配置 vue-router npm i vue-router@3
1.在src下建立 router 文件夹 在文件夹下建立 index.js index.js 内容如下
import Vue from "vue"
import VueRouter from "vue-router"
import About from "../pages/About"
import Home from "../pages/Home"
Vue.use(VueRouter) // 这里必须 use 否则 在 使用 router标签的时候 会报错
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:"/about",
component:About
},
{
path:"/home",
component:Home
}
]
})
2.注意 路由 组件的 文件夹 和 commponents 平级 叫 pages
2.在 main.js 中 引入 router const router from './router'
import Vue from 'vue'
import App from './App.vue'
import store from "./store"
import router from "./router"
Vue.config.productionTip = false
Vue.config.devtools = true
new Vue({
render: h => h(App),
store,
router:router,
mounted(){
// console.log("vm",this)
}
}).$mount('#app')
3.此时 网页路径中如果出现 # 说明 router 配置成功 http://localhost:8080/#/
4.多级路由配置
1.在 children 里面的 path 一定不要加 /
2.在 route-link 标签里面 to 要加上 父 路径 to="/home/news"
3.几个组件通过 传参 共用一个组件
1.参数通过 this.$route.query 这个对象获取 配合 计算属性 展现在页面中
2.这里的传参是指的 query 参数 home/news?id=001&title=msg 01
3.还有 一种 params 参数 home/news/001/msg 01
1.这种 params 参数需要去路由器设置
name: "content",
//想在 组件中使用 params 参数 必须 在这里 配置 组件中通过 $route.params.id 获取
//route-link to 中 {name:content} 必须用name 方式 用path 会发生错误
path: "content/:id/:title/:content",
component: Content
2.在 route-link to 中 必须配合 name 如果用 path 会出错
<!-- //如果用 params 必须使用 name 用 path 会出错 -->
<router-link
:to='{ name:"content",params:{...item}}'>{{ item.title }}
</router-link>
4.可以用 name 代替 path 需要在 router中定义 name 比路径 短一些 方便控制
<router-link
:to='{ name:"content", query: { ...item} }'>{{ item.title }}
</router-link>
5.路由page组件 获取 数据 要通过 this.$route 很麻烦 解决办法如下
1.配置 router index.js 让 这些参数 以 props 的方式传给 page 组件 组件在 props 中接受立即可以用
2.有三种配置方式
1.以对象方式
{path:"xx",compotent:Message,props:{name:"zs",age:30}}
2.布尔值 props = true
1.可以把 路由收到的 params 参数 通过 props 传给 Content 组件
2.但是 无论怎么设置 对 query 里面的参数 无动于衷
3.函数方式 props(route){} 这里的参数默认是 当前页面的 route
1.props({query:{id,title,content}}){return {id,title,content}}
2.在 Content 组件中 props:["id","title","content"] 直接使用
{
path: "news",
component: News,
children: [
{
path: "content",
component: Content,
props({query:{id,title,content}}){
return {id,title,content}
}
}
]
}
6.vue-router 对历史记录的影响 history
1.route-link 默认开启的是 push 模式 [可以后退] 如果不想有历史记录 可以
1.<route-link replace></route-link>
7.如何不用 route-link 实现 路由跳转 编程时导航
1.this.$router.push() name:"content",params:{...item} // 有历史
2.this.$router.replace() name:"content",params:{...item} //无历史
3.如何操作历史记录
1.this.rouer.back()
2.this.rouer.forward()
3.this.rouer.go(-1/2)
8.另外 从一个 组件切换到另一个组件 之前的组件会销毁不会缓存
1.如何不销毁保持挂载状态呢 [有时候 人家输入大半天 切一下回来 没了]
1.给 route-view 外包括 keep-alive 标签
//这里的 Content 是 组件的名字 export default {name:Content} 只有这个组件缓存 其他组件销毁
<keep-alive include="Content">
<router-view></router-view>
</keep-alive>
9.全局路由守卫
1.前置
//前置路由守卫
router.beforeEach((to,from,next) => {
// to 要去的路由 from 从哪个路由来 通过检查后 next 放行
// meta 里面存个人需要的 元数据 比如 该路由是否需要 鉴权
// 可以用 to.path to.name 等等去判断
if(to.meta.isAuth){
//该 路由 需要鉴权
if(localStorage.getItem("Auth") == "login"){
next() // 放行
}else{
console.log("需要登录")
}
}else{
// 不需要鉴权 直接放行
next()
}
console.log(to,from)
})
2.后置
//后置路由守卫
router.afterEach((to,from) => {
//后置路由守卫没有 next 因为已经放行了
document.title = to.meta.title ? to.meta.title:"no title"
})
3.独享路由守卫 只有前置 注意着只是一个 普通的函数 参数不是回调
{
path: "news",
component: News,
beforeEnter(to,from,next) {
console.log("访问 news 了 ")
next()
},
meta:{
isAuth:true,
title:"新闻"
},
children: [
{
path: "content",
component: Content,
props({query:{id,title,content}}){
return {id,title,content}
}
}
]
}
4.组件内守卫
1.该组件还没有渲染 this 为 undefiend
beforeRouteEnter(to, from, next) {
//从路由过来进入触发 如果是 在 页面通过标签 生成组件的方式 进入 无效 不会触发
console.log(11111111111111);
console.log(to, from);
console.warn(next);
//如果不调用 next 无法进入 当前页面
next();
},
2.beforeRouteUpdate()(to, from, next) {
//在该组件被 复用 的时候 举例 在 在 foo/:id foo/1 foo/2 之前互相跳转的时候 触发 可以访问this
}
//从路由过来 离开前 触发 如果是 在 页面通过标签 生成组件的方式 进入 无效 不会触发
beforeRouteLeave(to, from, next) {
console.log(222222222222);
console.log(to, from);
console.warn(next);
//如果不调用 next 无法离开 当前页面
next();
}
5.router 工作的 两种模式
1.mode:"history"/"hash"
6.关于 next 的几种操作U哦
1.next() 放行 自动去 to 的地方
2.next(path) 放行到指定路由
3.next(false) 会重新回到 from 的路由
4.next( new Error("xx") ) 会触发 onError 中注册的回调
10.router.onError(fn)
1.如果发生错误 会触发这个回调
11.路由的懒加载
1. 在 router 的 index.js 里面 默认直接 import 后 配置 routes [{path:"/",commpontent:Home}]
1.默认 import Home from "./commponents/Home"
2.懒加载 let Home = () => import("./commponents/Home")
UI for Vue
1.mobile
1.Vant 蚂蚁金服 https://vant-contrib.gitee.io/vant/#/zh-CN/button [不是蚂蚁金服吧 下面才是?]
2.Cube UI 滴滴
3.Mint UI 饿了吗
4.WeUI 微信 https://weui.io/
5.antd 蚂蚁金服 https://mobile.ant.design/zh
2.Pc
1.Element UI 饿了么
2.IView UI
News 和 Messge 共同子组件 Content.vue
<template>
<ul>
<li>{{ id || titleParams }}</li>
<li>{{ title || titleParams }}</li>
<li>{{ content || contentParams}}</li>
</ul>
</template>
<script>
export default {
name: "Content",
computed: {
//http://localhost:8080/#/home/news/content?id=001&title=news%201&content=本拉登被击毙
id() {
return this.$route.query.id;
},
title() {
return this.$route.query.title;
},
content() {
return this.$route.query.content;
},
//如果 在 route-link 的配置是 name 和 params 下面属性 必须name 用 path 出错
//http://localhost:8080/#/home/message/content/003/message%203/今日天气预报%20有小雨
idParams() {
return this.$route.params.id;
},
titleParams() {
return this.$route.params.title;
},
contentParams() {
return this.$route.params.content;
},
},
mounted() {
console.log(this.$route.query)
console.log(this.$route.params)
},
};
</script>
<style scoped>
li {
width: 100%;
font-size: 16px;
}
</style>
Message.vue 详细配置
<template>
<ul>
<li v-for="item in messageList" :key="item.id">
<!-- to 用字符 串模式 开启 v-bind 传入参数 等待 组件去接受 -->
<!-- <router-link :to = "`/home/message/content?id=${item.id}&title=${item.title}&content=${item.content}`">{{ item.title }}</router-link> -->
<!-- to 用 对象 模式 开启 v-bind 传入参数 等待 组件去接受 -->
<!-- <router-link
:to='{ path:"/home/message/content", query: { ...item} }'>{{ item.title }}
</router-link> -->
<!-- //如果用 params 必须使用 name 用 path 会出错 -->
<router-link
:to='{ name:"content",params:{...item}}'>{{ item.title }}
</router-link>
<!-- , query: { ...item} -->
</li>
<router-view></router-view>
</ul>
</template>
<script>
export default {
name: "Message",
data() {
return {
messageList: [
{ id: "001", title: "message 1", content: "今日天气预报 有大雨" },
{ id: "002", title: "message 2", content: "今日天气预报 有中雨" },
{ id: "003", title: "message 3", content: "今日天气预报 有小雨" },
],
};
},
};
</script>
多级路由配置 router index.js
import Vue from "vue"
import VueRouter from "vue-router"
import About from "../pages/About"
import Home from "../pages/Home"
import News from "../pages/News"
import Message from "../pages/Message"
import Content from "../pages/Content"
Vue.use(VueRouter)
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:"/about",
component:About
},
{
path:"/home",
component:Home,
children:[
{
path:"message",
component:Message,
children:[
{
name: "content",
//想在 组件中使用 params 参数 必须 在这里 配置 组件中通过 $route.params.id 获取
//route-link to 中 {name:content} 必须用name 方式 用path 会发生错误
path: "content/:id/:title/:content",
component: Content
}
]
},
{
path:"news",
component:News,
children:[
{
path:"content",
component:Content
}
]
}
]
}
]
})
3.页面中如何 对应 路径 和组件
1.App.vue 代码如下 在整个过程中 没有人为的去引用 组件 <About/> 是 vue-router 组件自己引用的
<ul class="nav fl">
<li><router-link class="rlink" active-class="active" to="/about">About</router-link></li>
<li><router-link class="rlink" active-class="active" to="/home">Home</router-link></li>
</ul>
<router-view class="con fl">我就是默认显示的内容</router-view>
2.具体显示的位置在 router-view 标签中间
3.router-link 点击 可以根据 to 的路径 去找相对的组件 会体现在 地址栏上 http://localhost:8080/#/home
4.active-class 把 样式定义好 vue 自己会引用
4.关于vue-router特征
1.所有的页面都有自己的 this.$route 属性 各不相同 但 都有一个共同的 this.$router 属性
5.关于一些常见错误处理
1.在有 <router-link> 页面中 报错 Unknown custom element: <router-link>
1.解决办法 在 router/index.js router的创造页面中 Vue.use(VueRouter) 就可以了
6.
Vue 脚手架 配置代理服务器 解决 跨域问题
1.方法一
1.devServer:{proxy:"http://127.0.0.1:5000"}
2.简单 当请求前端 axios 请求的时候 的时候 直接发给 127.0.0.1:8080 接受到后 本地如果有对应资源 直接返回 如果本地没有对应资源
3..直接转发给 pooxy 这个代理服务器 并且 是 整个网址
1.比如 axios 访问 http://127.0.0.1:8080/cards 代理服务器有 直接返回 没有 就访问 http://127.0.0.1:5000/cards
4.无法配置多个代理服务器 转发地址
2.方法二 重写 path 可以配置多个代理服务器
devServer:{
proxy:{
'/path1'://匹配 所欲 /path1 开头的请求路径 比如 127.0.0.1:8080/path1/cards
{
target:"http://localhost:5000",//转发服务器的具体路径
changeOrgin:true,//为 true 开启 说谎模式 告诉 target 服务器 我请求来自于 和 target 一样的host,
//默认 匹配后 会完全转发到 target 服务器 如 http:...:8080/path1/cards--->http:...:5000/path1/cards
//但是 这个路径重写 正则 匹配后 把 路径 path1 替换为 “” 如 http:...:8080/path1/cards--->http:...:5000/cards
pathRewrite:path => path.replace(/^\/path1/, '')
},
'/path2'://匹配 所欲 /path1 开头的请求路径 比如 127.0.0.1:8080/path1/cards
{
target:"http://localhost:5000",
changeOrgin:true,
pathRewrite:path => path.replace(/^\/path2/, '')
}
}
}
3.devServer proxy 工作流程
1.如果 只是配置 {proxy:"http://127.0.0.1:5000"} 所有的都会转发给 这个地址
1.但是在你代码访问的时候 你还是要假装访问 浏览器能接受的 同源地址 并不是说 在代码中直接访问 代理服务器
2.如果 必须 给 代理服务器的 根目录 发送 呢? 岂不是 全部线路 都必须走代理 有解决办法呢
1.可以假装在访问的时候 加上个特征 地址 比如 127.0.0.1:8080/abc?action=getData
2.再在proxy 里面 匹配 /abc
3.再在重新的时候 替换为 空 pathRewrite:path => path.replace(/^\/path2/, '')
3.如果本地地址有这个资源 直接访问本地地址资源 没有了才 往代理服务器转发
4.并且浏览器的数据包 还是显示发到 原来没有代理前的地址 也就是说浏览器根本就不知道
4.post 的问题 对于post数据 当body是 json 的时候 总是超时 需要配置 代理服务器如下 :[多了个 onProxyReq]
proxy: {
'/abc'://匹配 所欲 /path1 开头的请求路径 比如 127.0.0.1:8080/path1/cards
{
target: "http://192.168.0.11",
changeOrgin: true,
onProxyReq: function (proxyReq, req, res, options) {
if (req.body) {
let bodyData = JSON.stringify(req.body);
// incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
// stream the content
proxyReq.write(bodyData);
}
},
pathRewrite: path => path.replace(/^\/abc/, '')
}
},
before: require('./mock/mock-server.js')
}
1.基础知识
1.main.js
import Vue from 'vue'
import App from './App.vue'
import store from "./store"
Vue.config.productionTip = false
Vue.config.devtools = true
new Vue({
render: h => h(App),
store,
mounted(){
// console.log("vm",this)
}
}).$mount('#app')
1.如果模块化 引入 不开启 namespaced = true; 模块之间
1.在不开启命名空间的情况下
代码实例
Count.js Store Config
export default {
// namespaced:true,
actions: {
/**
在这里
this 是 stroe
context 是一个 有 dispatch commit state 的包裹对象 之所有有 dispatch 可以调用 actions 中 方法
为 actions 中的复杂逻辑做 准备 可能多个 dispatch 调用 这里面的方法 只能通过 dispatch 调用
没办法 通过 this 调用 自己对象的方法 因为这里 this 的指向 store 这里的方法 分别存放在
this._actions 和 this._mutations 通过 dispatch commit vuex 自己会找对应的方法
*/
increaseOdd(context, value) {
//这里想方法 state context.state.sum
if (context.state.sum % 2) {
context.commit("Increase",value)
}
},
increaseWait(context, value) {
setTimeout(() => {
context.commit("Increase",value)
}, 500)
}
},
mutations: {
// 在这里
// this 是 stroe
// context 是一个 只能看到 state 的包裹对象
//这里想方法 state context.sum
Increase(context, value) {
context.sum += value;
}
},
state: {
sum: 0
},
getters: {
bigSum(context){
return context.sum * 10;
}
}
}
todos 实例总结
版本一 父子组件传递信息的方式
1.项目开发流程
1.拆分空组件 app 挂空组件 首先层层级别组件 完成空组件的层层嵌套
2.再app中 开发整个静态页面
3.拆分 对应的页面部分 到对应的空组件[含css部分]
4.动态数据化
5.根据情况 绑定 事件
2.一个组件 数据放在组件本身即可
3.一些组件在用 放在他们的父组件
4.props适用于
1.父组件 给 子组件 通信
2.子组件 给 父组件 通信 [要去 父组件 给 子 组件 传递一个 函数 子组件调用函数 通信数据]
5.v-model 只能绑给于value属性的 因为默认是 v-model:value 的省略
1.v-model 不能绑定 props 传递过来的 数值 因为 props 是不可以修改的
2.虽然不能修改 但是把传过来的值 赋值给 data 的属性 改变data里面的属性 可以用来改变 显示渲染页面 本身的值不能改
6.props传过来的若是对象 可以绑定需改对象的属性 但是不建议
数据代理和数据劫持
5.data的两种写法 只要 data里的数值 发生变化 Vue 必定会重新解析模板
1.对象型 data:{}
2.函数行 data(){return {}}
1.非组件 哪种都行 组件必须用函数 因为可能一个页面 一个组件被多次调用 必须确保 data 对象 都各自有各自的地址
6.mvvm 模型
1.M model 模型 data中的数据
2.V View 视图 模板代码
3.VM ViewModel 也就是 Vue 实例
4.data中的所有属性都出现再 new Vue 的实例 Vm 上,Vm上的所有属性 包括原型 都可以在 View 中 都可以访问
7.vm种的数据代理
1.vm._data 完全等于 定义在 Vue({data:{name:"zs"}}) 里面的 data vm.name 调用 get方法 访问 vm._data 里面的 name
2.vm._data 又完全等于 Vue 中配置的 数据 于是就实现了 双向数据代理 变 vm.name set 设置 vm._data 影响 data:{}
3.data:{} 对象数据变化 影响 _data 用插值语法的时候 {{name}} name 又触发了 get 于是 双向绑定实现
4.数据劫持和数据代理的区别
1.Vue({data:{}}) 中的 data 被 let obs = new Observer(data) _data = data = obs 这就叫数据劫持 已经不是原来的了
2.vm.name 访问时候 要调用 get 方法 去 vm._data 中 访问 这叫数据代理 要的时候给什么数据 自己无法决定 找代理
4.重点
1.其实 运行后 _daba data 虽然相等 但其实 这个 data 已经不是 new Vue(data:{}) 这个 data 了 已经进行了 observer 转换
2.其实现的基本原理如下 但没有进行 data 属性的 递归检测
var data = {
name: "zs",
age: 30
}
function obServer(obj) {
let keys = Object.keys(obj)
keys.forEach((item) => {
console.log(item)
Object.defineProperties(this, {
[item]: {
set(val) {
console.log("哦要去解析模板了")
obj[item] = val
},
get() {
console.log("是谁在访问我")
return obj[item];
}
}
})
});
}
let obs = new obServer(data)
let _data = data = obs;
3.利用闭包 实现了对 原始 data 对象的 封装 然后重新赋值给 _data 和 data 这样无论 谁变化 都会被检测到 然后再把_data 中的
1.属性 用数据代理的模式挂在到 vm 上面 于是就实现了 三级联动效果 vm.name->_data->闭包中的原始 data数据
4.vm会递归用Observer监视data所有层级的数据
5.对于 data 后增加的数据 vm 默认不做响应式处理 如果想要后增加的数据能响应式处理
1.Vue.set(target,perpertyName/index,value)
2.vm.$set(target,perpertyName/index,value)
6.如何对数组中的数据实现响应式的呢
1.Vue对数组的 push pop shift unshift splice sort reverse 进行了包装 做了两件事
1.首先调用数组的原始对应方法处理
2.重新解析模板
2.用 Vue.set(arr,index,value) vm.$set(arr,index,value) 也可以
3.直接用 vm.arr1[0] = "zs" 这样不行 因为 Vue 没有对数组做 数据代理 [没有setter getter]
1.但是对数组元素 如果是对象 仍然做 数据代理 vm.arr1[0].student.name = "zs" 可以响应式处理
7.特别注意 Vue.set vm.$set 无法在 vm 上 和 vm._data 的根数据 添加属性 也就是说必须添加到 data.student.xxx
1.不能是 data.newProperty
计算属性和监视属性
8.计算属性 两种都可以
1.里面的get调用时机
0.定义:属性不存在 靠计算获取
1.原理:借助 Object.defineProperty 方法的 getter setter
1.初次读取fullname
2.fullname 所依赖的 data数据发生变化的时候
2.计算属是有缓存的 methods的方法fullname2 没有缓存 所以 如果同时调用 5 次 fullname get 只调用了一次 fullname2 调 5 次
computed:{
fullname:{
get () {
return this.name
}
set (value) {
要想改 fullname 直接改不行 必须修改依赖的 data 里面的属性 然后 get的时候才能算出最新的属性
}
},
age(){
return 25;
}
}
3.计算属性 也会出现在 vm上 从表面上看 和 data里面的属性 没有分别 但是 vm_data 里面有 data里的属性 没有 计算属性
4.只考虑读取 不考虑改变 可以用简写方式 fullname(){return this.name} 来代替 get方法
1.所以计算属性 看似是个 方法 其实是个属性
5.计算属性定义后 可以在 method computed 等地方像普通属性一样 用 this 来引用 可以属性+属性 出新的结算属性
9.监视属性
watch:{
name:{
immdiate:true,
handler(newV,old){
console.log(newV,old)
}
}
}
2.等价于
vm.$watch("name",{
immediate:true,
handler(newV,oldV){
console.log(newV,oldV)
}
})
3.深度监视 对于 复合型数据 默认Vue 不会去深度监视 除非打开 deep:true
1.Vue 本身可以监视 data 里面的对象类复合型数据 可以深度监视 但是 watch 默认不可以
4.简写形式 [如果不需要 immediate,deep] 可以简写为函数
watch(){
name(newv,oldv)
{
console.log(newv,oldv)
}
}
vm.$watch("name",function(newV,oldV){
console.log(newV,oldV)
})
10.监视属性和计算属性的区别
1.都可以计算 但是 计算属性无法开启异步模式 监视属性可以开启异步模式 比如 属性发生变化后 一秒钟后处理
2.computed 可以完成的 watch 都可以完成 但是 反过来不行
3.在Vue中 何事用箭头何事不用
1.凡是被Vue管理的函数[methods computeed watch] 不要写成箭头函数 凡是用到 [setTimeout ajax axios promise] 要写箭头
2.这样可以确保this指向Vue
过滤器和表单数据的收集
11.表单数据的收集
1.input:text v-model="user" 正常收集
2.input:radio v-model="sex" value="male" 收集的是 value 值 所有必须给相同name的不同的radio 配置 value 值
3.input:checkbo v-model="hobby"
1.如果没有配置 value 值 收集的就是 checked data:{hobby:""} hobby:false
2.配置了 value
1.data:{hobby:""} 配置的非数组 收集的 仍然是 checked hobby:false
2.data:{hobby:[]} 配置的数组 收集的 value hobby:["smoking","drink","peom"]
4.input:select v-model 配置在 select 最后收集的是 option 的value 值
12.过滤器
1.可以通过管道符无限传递 可以用在 插值 和 v-bind 不可以用下 v-model
filters:{
addHello(val,paraName){
//val 为管道符传过来数值 paraName 为调用过滤器传来的参数
return val + "hello ";
}
}
2.全局注册 Vue.filter(filterName,callback)
生命周期 [生命周期函数 自己不用调用 Vue 根据情况调用的函数]
13.生命周期
1.beforeCreate
1.无法通过vm访问data的数据和方法 数据代理还没有开始
2.created
1.可以通过vm访问data数据和方法
3.beforeMounted
1.未经编译的dom就是 {{}} 可以操作 但最终是无效的 挂载后被编译的 dom 替换
4.mounted
1.元素已经挂载 可以发送 ajax 启动定时器 绑定自定义事件 订阅消息
2.将内存中的虚拟dom转为 vm.$el 并把 真实的dom 插入页面
5.beforeUpdate
1.数据是新的 但页面时旧的
2.拿新数据生成 虚拟dom 和 内存中的 dom diff 运算 然后完成 model->view 的更新
6.updated
1.数据时新的页面也是新的
7.beforeDestroy
1.清除定时器 取消订阅
2.这里可以操作数据 但是 已经不再渲染解析模型了 没有意义看不到
8.Destroy
1.销毁后 自定义事件失效 但原生事件仍存在
9.activated
1.激活组件时候
10.deactivated
1.失去激活的时候
11.this.$nextTick(fn)
1.下一次渲染的时候 执行 fn
组件和脚手架配置
0.官方脚手架的安装
1.参考 https://cli.vuejs.org/#getting-started
2.npm install -g @vue/cli 安装脚手架
3.vue create my-project 创建项目
1.非单文件组件
1.使用组件的三个步骤
1.定义组件
let school = Vue.extend({
template: ` <div>
<span>{{name}}</span></br>
<span>{{address}}</span></br>
<span>{{number}}</span></br>
<hr />
</div>`,
data() {
return {
name: "25 小学",
address: "独山大道",
number: "258 persons"
}
}
})
2.定义组件 可简写为 let school = options
2.注册组件
1.局部注册 new Vue({el:"#app",components: {school}})
2.全局注册 Vue.component("school2",school)
3.使用组件[在html中写组件标签]
2.如何定义一个组件
1.let componentName = Vue.exend({options}) options 和 new Vue 的 options 几乎一样 也有区别 如下
1.不能写 el 属性 因为组件不是固定给谁用的
2.data 必须写成函数式的 因为这样一个页面多组件引用时候 能包装 data中的数据互相独立
3.template 写组件结构 new Vue 也可以不要el 有 template 然后 vm.$mount("#app") 到 div.#app
3.组件套组件 let app = Vue.extend({components{school}}) let school = Vue.extend({components{student}})
1.app->school->student
2.let app = Vue.extend(template:"<school/>",{components{school}})
2.单文件组件
3.组件的构造函数 VueComponent
1.每次 Vue.extend() 创建的组件 实际是一个 每次都不一样的 VueComponent 函数,也就是说 Vue.extend() 返回的是一个 let sub =
2.的函数 当页面中调用 <schoool/> 的时候 Vue会帮我们把这个函数 new 出实例
3.组件简称 vc Vue 简称 vm
组件引用实例 Vue2 Vue3
//Content.vue
<template>
<div>
<div class = "con">
</div>
</div>
</template>
<script>
export default {
name:"Content"
}
</script>
<style>
.con{
width:800px;
border:1px solid #ccc;
border-radius: 8px;
height: 800px;
margin: 10px auto;
}
</style>
//Header.vue
<template>
<div class="head">
<ul>
<li v-for="(item,index) in category" :key="index"><a href="#">{{item}}</a></li>
</ul>
</div>
</template>
<script>
export default {
name:"Header",
data(){
return{
category:["家电1","玩具","促销打折","电动汽车","内衣内裤","越野配套"]
}
}
};
</script>
<style>
.con{
width:800px;
border:1px solid #ccc;
border-radius: 8px;
height: 800px;
margin: 10px auto;
}
</style>
//App.vue
<template>
<div>
<Header></Header>
<Content></Content>
<div class="footer"></div>
</div>
</template>
<style>
.footer{
background: pink;
width:800px;
height: 100px;
margin: 5px auto;
}
</style>
<script>
import Header from "./views/Header.vue";
import Content from "./views/Content.vue";
export default{
name:"App",
components:{
Header,
Content
},
}
</script>
//main.js vue 3
const { createApp } = require("vue");
// import { createApp } from 'vue'
import App from './App.vue'
let myapp = createApp(App);
console.log(myapp)
myapp.mount('#app')
//main.js vue 2
import Vue from 'vue'
import App from '../App.vue'
new Vue({
render: h => h(App),
}).$mount('#app')
4.一个重要的内置关系
1.VueComponent.prototype__proto__ = Vue.prototype
5.关于 vue 2 main.js 的分析
1.主要代码如下
new Vue({
render: h => h(App),
}).$mount('#app')
2.这里主要靠 render 函数来渲染
3.如果不用这个函数 用 template:"<h1>Hello</h1>" 如何 答案是目前不行 因为默认引入的 vue是残缺版的 不带 这种文本html的解析
4.功能 如果需要像在非独立组件那样的 template 里面解析 需要引进一个 完整版本的 引入的路径不能是默认的 require("vue/dist/vue")
new Vue({
template:"<h1>hello</h1>"
}).$mount('#app')
1.可以在页面中解析出 hello
5.那残缺版的 render 是怎么工作的呢 下面是 h 参数 是一个函数 把你传进去的 App组件解析成 html 并渲染到 页面
ƒ (a, b, c, d) {
return createElement$1(vm, a, b, c, d, true);
}
6.查看 vue 包 package.json 你会发现这句话 "module": "dist/vue.runtime.esm.js", 默认引入这个vue 没有模板解析器 运行时vue
1.因为没有模板解析器 所以不能使用 templat 配置项 需要 render(h) 受到的 h 也就是 createlement 去指定具体内容
6.关于 Vue 官方脚手架的配置文件
1.默认Vue隐藏了 脚手架的胚子和 webpack.config.js 可以用命令查看 vue inspect > output.js 查看
2.但是这里只能查看 不能修改 要想修改 只能改 Vue.config.js 配置文档地址 https://cli.vuejs.org/zh/config
其他
琐碎知识
1.数据代理 数据劫持 怎么实现的的
1.Object.defineProperty(obj,"age",{writable:ture,enumerable:true;configable:true,get(){},set(){}}) 默认全部为 false
1.let Object.defineProperty 当有仍访问 定义的属性 就会触发 get 写 触发 set
2.chrome浏览器查看 属性值 是 ... 需要点一下 才能看到 有一种现用现取的感觉 [还可以看到 有一个 get 方法 为谁服务的提示]
2.在Vue模板中 undefined 不渲染 读出数值 属性 只要是 undefined 不渲染 比如 从一个对象上 读一个不存在的属性 返回 undefined
3.在执行 import 引入的时候 具有像 var 一样的 变量提升效果 不管你写在哪里 中间掺杂了 多少代码 总会先扫描 import 并执行
1.脚手架和浏览器都一样的效果 下面的执行结果 // a b 2222
import "./a.js"
console.log("2222")
import "./b.js"
4.this.children this.parent [通信]
1.this.childre 可以获得当前 所有子组件的 vc 对象实例 可以看到所有数据和方法
2.this.parent 可以获得父组件的一切
2.通过 children parent 居然可以实现 响应式修改
5.this.$listeners [通信] 可以获取父组件的 所有除了 原生事件的方法 也就是所有的自定义事件
1.如果自己不处理 可以继续传给孙 还要继续传给子组件内部的其他组件,就可以通过 v-on=“$linteners”
6.:abc.sync = "num"
1.在父种 对子 进行 <Person :abc.sync = "num"></Person> 等价于下面
1.<Person :abc="num" @update:a="val=>num=val"></Person>
2.在 子 接 props:["abc"] 触发 this.$emit("update:abc","newValue") 父就会变化
1.update:abc 居然是个函数 哈哈哈
组件通信总结
1.props
1.一般属性
1.父给子传数据
2.传递函数 [子给父传数据]
3.props 的形式
1.props:["a","b"]
2.props:{a:String,b:Number}
3.props:{name:{type:String,defalut:"zs"}}
4.路由传参的 props
1.props:{name:"zs",age:30}
2.props:true [会传递 papams 参数给 props]
3.props:function({这里面可以是 query params 的对应 格式化数据}){return 通过 筛选 返回给 props}
2.自定义事件 子触发 父在子组件上 定义的 事件 传递数据给 父
3.全局事件总线 Vue.prototype.$bus = this; 万能
4.pubsubjs 万能
5.vuex 万能
6.slot
1.匿名
2.具名
3.作用域 子传父
7.v-model 子 传 父
1.父给子
1.<Son v-model="msg" /> 等价于
<span>{{ msg}}</span>
<Son :value="msg" @input="msg = $event" />
2.子给父
<input type="text" :value="value" @input='$emit("input",$event.target.value)'>
1.这里 props:["value"] 父中 把 value 传过来 绑定自定义 input 事件
2.子 input 在 input 事件中 触发 父的 input 自定义事件 并把当前的value值 传给 父
3.所以 用 v-model 实现了 子给父 传参
8.sync 子 传 父
1.父引入子组件 <Son msg.sync></Son>
1.等价于 <Son :msg @update:msg="msg = $event"></Son>
2.子触发 <button @click="$emit("update:msg,'hello world'")">直接影响model</button>
1.触发事件 后 会 改变msg的值
9.$attrs 和 $listeners
1.<button v-on="$listeners" v-bind="$attrs">直接影响model</button>
2.上面可以把 父元素 传的 所有属性 绑定到自己身上 注意 v-bind 在这里不能换位 :
3.v-on 可以把 父元素绑定给子元素的事件 绑定到自己身上 在自己身上直接触发 这里的 v-on 不能替换为 @
4.比如 父 <Son @click = "test" :title="title" :abc="123"/>
1. 子 <button v-on="$listeners" v-bind="$attrs">直接影响model</button> 此时 子点击 会触发test函数 就好像 .native 了
2. 子在浏览器中 <button title="hello" abc="123">直接影响model</button>
Vue 3
Vue 2 和 3 的不同点
1.main.js 的创建方式
1.vue 2
new Vue({
render:h=>h(App)
}).$mount("#app")
2.vue 3
createApp(App).mount("#app")
2.关于属性 方法 等等
1.vue 2 data methods
2.vue 3 setup
1.setup 的 返回值
1.对象 里面的 属性 方法 在模板中可以直接使用
2.返回一个渲染函数 可以自定义渲染内容
0.import{h} from "vue"
1.return ()=>h("h1","hello from rander h")
1.页面会直接显示 <h1>hello from rander h</h1>
3.注意事项
1.v2 中 的 data method 可以访问到 v3 setup 的属性和方法
2.v3 的 setup 访问不到 v2 中的 data methods 的属性和方法
3.如果有 data 和 setup 重名 以 setup 的为准
4.所以不建议混写
4.setup 不能是一个 async 函数 因为 async 函数 返回的是一个 promise 对象 v3 看不到 当然 里面的函数 不能是 await
1.因为 await 必须在 async 函数中 才能出现
3.数组修改数据的响应式
1.v2 中修改数组 得用方法才能做到响应式 直接修改 hobby[0] = "study" 不行
2.v3 中 用 reactive 函数 可以直接修改 直接修改 hobby[0] = "study" 可以
4.v2 options Api [配置项 api]v3 compostition Api [组合式api]
v3 独有的
1.refimpl 引用实现对象
1.let naem = ref("zs") 会被包装成一个引用实现对象
2.修改的时候 changeName(){name.value = "ls"} 直接 name = "ls" 是不行的 因为此时 name是 refimpl
1.但模板中引用的时候 不需要 value v3会在解析的时候 自己调用
3.对于对象 也用 ref let job = ref({type:"pc",salary})
1.访问的时候 job.value.type // pc
4.当处理一般数据的时候 ref 用的 get set 当处理对象数据的时候 用的 proxy 代理 所以不用 job.value.type.value
1.利用了v3自己的一个函数 reactive
2.reactive 函数
1.定义一个对象类型的 响应式数据 基本数据类型用 ref
2.用法 let 代理对象 = reactive(普通对象/数组)
3.reactive 定义的 响应式类型 是深层次的 内部基于 ES6 的 proxy 实现
4.let job = ref({}) let job2 = reactive({}) job.value.xx 等价于 job2.xx
1.ref 处理 对象数据 求助了 reactive 函数
2.
3.对于使用到的函数需要 去 vue 中 引用 imprt {ref,reactive} from "vue"
4.v3 数据代理的 基本原来
1.雏形如下
let person = { name: "zs", age: 18 }
let p = new Proxy(person, {
get(target, propertyName) {
console.log(`someone get ${propertyName}`)
return target[propertyName]
},
set(target, propertyName, value) {
console.log(`someone set ${propertyName} to ${value}`)
target[propertyName] = value;
},
deleteProperty(target, propertyName) {
console.log(`someone delete ${propertyName}`)
return target[propertyName]
}
})
2.升级版如下
let person = { name: "zs", age: 18 }
let p = new Proxy(person, {
get(target, propertyName) {
console.log(`someone get ${propertyName}`)
return Reflect.get(target, propertyName)
},
set(target, propertyName, value) {
console.log(`someone set ${propertyName} to ${value}`)
Reflect.set(target, propertyName, value)
},
deleteProperty(target, propertyName) {
console.log(`someone delete ${propertyName}`)
return Reflect.deleteProperty(target, propertyName)
}
})
3.set 的触发 包括添加数据 修改数据
1.但是如果用 Object.defineProperty/Object.defineProperties 添加数据不触发的 只有修改触发
2.因为对属性的定义是一个一个的 没有添加的属性 就无法定义 所以当然不能够触发了
1.Object.defineProperty(obj,"name",{set()})
2.Object.defineProperties(obj,{name:{set()}})
4.)
5.window.Reflect 里面有不少 Object 的属性 但是部分做了修改
1.Reflect.defineProperty(obj,"name",{})
2.Reflect.get(Obj,"name")
3.Reflect.set(Obj,"name",value)
4.Reflect.deleteProperty(Obj,"name")
5.Reflect.defineProperty
1.重复定义不报错 但是 Objec.defineProperty 重复定义报错
6.关于setup
1.setup的执行时机
1.在beforeCreate之前执行 this 为 undefined
2.setup(props,context)
1.需要在 props:["name"] 声明并接受 接受后会出现在这里
2.context
1.attrs 没有在 props 接受的 属性 相当于 this.$attrs
2.slots 收到的插槽内容 相当于 this.$slots
3.emit 触发父给子的插件自定义事件 相当于 this.$emit
7.关于计算属性
import { reactive,computed } from "vue";
export default {
name: "App",
setup() {
let person = reactive({
first: "zhang",
last: "san",
});
//计算属性简写
// person.fullname = computed(() => person.first + " " + person.last)
//计算属性需要读写
person.fullname = computed({
get(){
return person.first + " " + person.last
},
set(v){
let arr = v.split(" ")
person.first = arr[0]
person.last = arr[1]
}
})
return { person };
},
components: {},
mounted() {
console.log(this);
},
};
8.关于监视属性
1.基本写法
//简单写法 可以同时 watch 多个属性 一次一次 watch
watch(person,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true})
watch(person1,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true})
//也可以同时监视多个 此时 newValue 和 oldValue 是一个数组值 记录着每个被监视属性对应的 值
watch([person,person1],(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{immediate:true,deep:false}) 无法关闭 深度监视
2.大坑
1.监视 reactive 类的数据 newValue 和 oldValue 一样
2.默认开启了 deep:true 并且无法关闭
3.无法监视基本数据类型 想要监视 需要写成 ()=>person.fullname 函数模式
1.监视单个基本类型属性
watch(()=>person.fullname,
(newValue, oldValue) => {
console.log(newValue, oldValue);
},
{ immediate: true }
);
2.监视多个基本类型属性
watch([()=>person.first,()=>person.last],
(newValue, oldValue) => {
console.log(newValue, oldValue);
},
{ immediate: true }
);
4.监视对象中的 某个对象属性 也会出现 new old 相同的情况
3.watchEffect(() => {let a = person.name})
1.不具体监视哪个属性 回调中用到哪个 属性 就会监视哪个属性 只要被监视属性变化 就会重新计算一次 很像 v2 的 computed 属性
2.但computed 更注重返回值 而 watchEffect 更注重 过程 不需要返回值
8.关于生命周期
1.把 v2 的 beforeDestroy destroy 替换为 beforeUnmounted unmounted
2.用配置项模式和 setup 平级 setup 最先执行 其他和 v2一样
3.如果用组合式api在 setup 里面 应用生命周期函数 beforeCreate created 没有提供 对应的 组合式api setup本身可以对等
1.onBeforeMount onMounted onBeforeUpdate onUpdated onBeforeUnmount onUnmounted
9.hooks 类似 v2 的 mixin
1.定义 SavePoint.js
import {reactive,onMounted,onBeforeMount} from "vue";
export default function(){
let point = reactive({
x:0,
y:0
})
function savePoint(e){
point.x = e.pageX
point.y = e.pageY
console.log(point)
}
onMounted(()=>{
window.addEventListener("click",savePoint)
})
onBeforeMount(() =>[
window.removeEventListener("click",savePoint)
])
return point;
}
2.需要的组件引入 import SavePoint from "./hooks/SavePoint"
3.在 setup 中 执行 ponint = SavePoint()
4.在 setup 中 return {point}; 模板中就可以引入 point 了
10.toRef 和 Ref
1.toRef 可以把 Person.name 这些前缀去掉 需要在 return 返回需要的属性 可以自定义
return {
first: toRef(person,"first"),
last: toRef(person,"last"),
fullname: toRef(person,"fullname"),
nali:toRef(person.scholl,"address"),
handleClick,
point };
},
2.同ref 不同 比如 xing = ref(person.first) 是把 zhang 这个字符串 通过ref函数 包装成 refimpl 对象 虽然表面上也能改变
1.甚至能做响应式 但本质上 xing 以后 修改的已经不是 person.first 只是 zhang 的ref包装对象
3.然后 toRef 是在把数据转成响应式的同时 和 Person.first 挂钩 流程如下:读 zhang 去找 ref 的 get 在 get里面再去找
1.person.first 内部已经做了 中转
4. ...all = toRefs(person) 把person的所有属性 都拆出来了 可以直接使用first last fullname
11.shallowRef 和 shallowReactive
1.shallowRef
1.普通数据 shallowRef 和 Ref 没有任何区别
2.对象用 shallowRef 不会求助于 reactive
2.shallowReactive
1.对象只做第一层属性的响应 其他的不管
12.readonly shallowReadly
1.只能修改/第一次禁止修改
13.toRaw markRaw
1.toRaw 把 响应式对象[reactive] 变成原始的 Object 普通 ref 对象转换不行
2.markRaw 标记一个对象永远为原始的对象 不做响应式处理 就算挂到响应式对象上 也不做响应式处理
1.但是对象本身是可以改变的 只是不做响应式处理 比 readonly稍微好点儿
14.customRef 自定义 Ref
1.具体代码
function myRef(value,delay){
let timer = null;
return customRef((track,trigger) => {
return {
get(){
//告诉get要追踪这个数值 如果不告诉 只调用一次 完成任务 track 后 需要值班
track()
return value;
},
set(newValue){
//防抖
clearTimeout(timer)
timer = setTimeout( ()=> {
//把设置的新数值赋值给 value
value = newValue
//告诉 模板需要重新解析了
trigger()
},delay)
}
}
})
}
15.provide 与 inject [通信] [父组件与后代组件的通信]
1.父 provide("car",car)
2.子 let car = inject("car") return {car}
3.原来 v2 也支持
父
provide(){
return {
provideName:"provideName - hello",
age:18
}
子
export default {
name:"MyPerson",
inject:["provideName","age"]
2.子可以当作属性之间使用 类似 props 接到的数据
}
16.响应数据的判断
1.isRef isReactive isReadonly isProxy
17.fragment
1.v3 之所以可以不用 根标签 是因为当v3发现用户没有用根标签的时候 会包括一个 fragment 并在渲染时候去掉
18.teleport
19.<teleport to="body/#myid"><div></div></teleport>
1.div 不管在 子 孙 后代 哪个组件里面 只要在外部包括了 teleport 就会根据 to 传送到 那个 html标签
20.全局api的变化
1.v3 没有了Vue 很多api 都移动到了 let app = createApp(App) 的 app 上了
1.app.config.xx
2.app.component
3.app.directive
4.app.mixin
5.app.use
6.app.config.globalProperties 相当于 Vue.prototype
21.其他的一些变化
1.必须写data 函数
2.过渡类型 v-enter ---> v-enter-from
1.v-leave --->v-leave-from
3.移除了 keyCode 作为 v-on 的修饰符
4.移除了 v-on.native
1.用 emits:["click","demo"] 如果这里没有接受 click 事件 那么就是 原生的 如果接受了 就是父给子自定义的
5.移除了 过滤器