起步
创建项目
Vite(推荐使用)
Vite
是一款作者意图取代webpack
的工具,目前仅为vue3
提供服务,同样支持热更新- 其工作原理是利用
ES6
的import
会发送请求去加载文件的特性,拦截这些请求并做一些预编译,从而省去webpack
冗长的打包时间
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
Vue-Cli(使用前请务必升级)
$ vue create <project name>
$ cd <project-name>
$ npm install
$ npm run dev
项目主要文件
index.html
index.html同样有一个id为app
元素用来挂载,通过 script
引入入口文件
App.vue
App.vue最大的不同点在于我们不再需要像2.X一样,在最外层再裹一层 div
作为模板根节点
main.js
main.js跟2.X有较大差异,3.X从 vue
引入 createApp
方法,再把 App
这个组件传入 createApp
方法 并进行挂载
模板指令
1.v-model
-
prop和event的默认名称更改:value -> modelValue input -> update:modelValue
-
移除 v-bind 的 .sync 修饰符与组件的 model ,并替换为 v-model
-
3.x版本下的 v-model 可以在同一个组件上进行多个绑定
-
添加了创建自定义 v-model 修饰符的功能
2.X语法
<!-- parent -->
this.$emit('update:title', newValue);
<!-- children -->
<ChildComponent v-model="pageTitle" />
<!-- would be shorthand for: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
<!-- 可简化为.sync -->
<ChildComponent :title.sync="pageTitle" />
3.X语法
<!-- parent -->
this.$emit('update:title', newValue);
<!-- children -->
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- would be shorthand for: -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
迁移策略
<!-- 把代码中的.sync进行替换 -->
<ChildComponent :title.sync="pageTitle" />
<!-- to be replaced with -->
<ChildComponent v-model:title="pageTitle" />
自定义修饰符
<!-- html -->
<div id="app">
<my-component v-model.capitalize="myText"></my-component>
{{ myText }}
</div>
// js
const app = Vue.createApp({
data() {
return {
myText: ''
}
}
})
app.component('my-component', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
methods: {
emitValue(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
},
template: `<input
type="text"
:value="modelValue"
@input="emitValue">`
})
app.mount('#app')
2.key属性(唯一标识:为了高效地更新虚拟DOM)
1.key
属性不再建议在 v-if
v-else
v-else-if
上使用,Vue3.x将会自动生成
2.X语法
<div v-if="condition" key="yes">Yes</div>
<div v-else key="no">No</div>
3.X语法
<div v-if="condition">Yes</div>
<div v-else>No</div>
2.如果主动添加 key
属性,必须要使用唯一值,不再允许使用 key
来强制分支重用
2.X语法
<div v-if="condition" key="a">Yes</div>
<div v-else key="a">No</div>
3.X语法
<!-- Vue 3.x (recommended solution: remove keys) -->
<div v-if="condition">Yes</div>
<div v-else>No</div>
<!-- Vue 3.x (alternate solution: make sure the keys are always unique) -->
<div v-if="condition" key="a">Yes</div>
<div v-else key="b">No</div>
3.<template v-for>
key 应放在 <template>
标签上(2.x放在子标签上)
2.X语法
<template v-for="item in list">
<div :key="item.id">...</div>
<span :key="item.id">...</span>
</template>
<template v-for="item in list">
<div v-if="item.isVisible" :key="item.id">...</div>
<span v-else :key="item.id">...</span>
</template>
3.X语法
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>
<template v-for="item in list" :key="item.id">
<div v-if="item.isVisible">...</div>
<span v-else>...</span>
</template>
3.v-if
与 v-for
一同使用时优先级更改
在2.x中,v-if
与 v-for
一同使用时,v-for
的优先级大于 v-if
,3.x中,v-if
优先级将大于 v-for
迁移策略
由于语法上的歧义,避免在同一元素上使用它们
4.v-bind
现在对绑定顺序敏感
1.v-bind
的绑定顺序将影响渲染结果,在2.x语法中,vue假设开发人员希望单个属性始终覆盖定义的内容,在3.x语法中,开发人员将获得更多的控制权
2.X语法
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="red"></div>
3.X语法
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>
迁移策略
确保先定义v-bind
属性,再定义各个属性
5.ref
与 v-for
一同使用时,将不再为 $refs
注册数组
1.在2.x中,当 v-for 用于元素或组件的时候, r e f s 的 引 用 信 息 为 包 含 D O M 节 点 或 组 件 实 例 的 数 组 , 即 : c o n s o l e . l o g ( t h i s . refs 的引用信息为包含DOM节点或组件实例的数组,即:console.log(this. refs的引用信息为包含DOM节点或组件实例的数组,即:console.log(this.refs)可以看到打印出来的是一个数组
2.在3.x中,$refs将不再自动创建数组,如果要从单个绑定中检索多个refs,需要把 ref 绑定到一个函数上以获得更大的灵活性
<div v-for="item in list" :ref="setItemRef"></div>
export default {
data() {
return {
itemRefs: [] //不一定是数组,也可以是一个对象
}
},
methods: {
setItemRef(el) {
this.itemRefs.push(el)
}
},
beforeUpdate() {
this.itemRefs = []
},
updated() {
console.log(this.itemRefs)
}
}
Composition API
2.X存在的问题
Vue3.X兼容2.X的写法,在2.X写法上,我们发现同一功能的业务逻辑非常分散,例如实现删除功能所需的数据放在 data
里,实现该功能的方法可能放在 methods
里,也有可能放在 computed
或 watch
里,在大项目中会造成后期维护困难,例如:
<script>
export default {
name: 'App',
data() {
return {
msg: '模块一',
msg2: '模块二'
}
},
methods: {
add() { '模块一的方法' },
remove() { '模块二的方法' }
},
computed: {
'模块一',
'模块二'
},
watch: {
'模块一',
'模块二'
}
}
</script>
3.X解决方法
3.X为了解决代码碎片化的问题,引入了 Composition API,它能把同一功能的业务逻辑整合在一起,极大地提高了代码的可维护性,例如:
代码实现:
<!- App.vue ->
<template>
<form action="">
<input type="text" v-model="state2.list.id">
<input type="text" v-model="state2.list.name">
<input type="text" v-model="state2.list.age">
<input type="submit" @click="add" value="新增">
</form>
<ul @click="operation" id="list">
<li v-for="(item, index) in state.list" :key="index">
<span style="margin-right: 10px">{{ item.name }}-{{ item.age }}</span>
<button id="edit">修改</button>
<button id="del">删除</button>
</li>
</ul>
</template>
<script>
import rowOperation from './rowOperation.js';
import addOperation from './addOperation.js';
export default {
name: 'App2',
setup() {
let { state, operation } = rowOperation();
let { state2, add } = addOperation(state);
// 所有需要在视图中展示的数据和使用的方法都必须return出来
return {state, operation, state2, add};
}
}
</script>
// rowOperation.js 行操作
import {reactive} from 'vue';
function rowOperation() {
let state = reactive({
list: [
{id: 0, name: 'zs', age: 15},
{id: 1, name: 'ls', age: 18},
{id: 2, name: 'ww', age: 19}
]
})
function operation(e) {
e = e || window.event;
let target = e.target || e.srcElement; //兼容IE8
if(target.parentNode.tagName.toLowerCase() == 'li') {
let li = document.querySelectorAll('li'); //querySelectorAll返回类数组
let index = Array.prototype.indexOf.call(li, target.parentNode);
switch(target.id) {
case 'edit':
edit(index);
break;
case 'del':
del(index);
}
}
}
// 编辑
function edit(index) {
...
}
// 删除
function del(index) {
...
}
// 记得把在视图展示的数据和用到的方法return出来
return {state, operation};
}
export default rowOperation;
// addOperation.js 新增操作
import {reactive} from 'vue';
function addOperation(state) {
let state2 = reactive({
list: {
id: '',
name: '',
age: ''
}
})
function add(e) {
e.preventDefault();
...
}
return {state2, add};
}
export default addOperation;
Composition API 的本质
Composition API 可以称为组合API,也可称之为注入API,它的本质是把 Composition API 暴露出来的数据和方法注入到 data
和 methods
中,例如:
import {ref} from 'vue';
export default {
data() {
return {
msg1: '123'
//msg2: 0
}
},
methods: {
myFun1() {
...
}
// myFun2() {
// ...
// }
},
setup() {
let msg2 = ref(0);
function myFun2() {
...
}
return {state, myFun2};
}
}
setup()
的执行时间与注意点
Vue3.X 允许 Composition API 和 Option API 混合使用,但是 setup() 函数的执行时间是在 beforeCreate 之前,因此无法调用 data 和 methods ,vue3.X为了避免在 setup() 中误用 data 和 methods ,this 被设置为 undefined ,即:
export default {
data() {
return {
msg: 'hello'
}
},
methods: {
myFun() {
...
}
},
setup() {
console.log(this); //undefined
console.log(this.msg); //error
this.myFun(); //error
}
}
注意:setup()
不能是异步函数!!例:
async setup() { //页面内容将无法展示
....
}