vue3总结
# vue3性能提升
- 打包大小减少41%
- 初次渲染快55%,更新渲染快133%
- 内存减少54%
# 源码升级
- 用proxy代替defineProperty实现响应式
- 重写虚拟DOM实现
- 引入tree shaking
# 拥抱TS
- vue3支持TypeScript语言
# 其他一些列新特征
# 使用vue/cli创建web项目(需要vue/cli4.5.0以上的版本)
- 配置淘宝镜像:
npm config set registry https://registry.npm.taobao.org
- 安装vue/cli:
npm install -g @vue/cli
- 切换到你要创建项目的目录,然后使用命令创建项目:
vue create xxx
- 启动项目:
npm install serve
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
> Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
Manually select features
2
3
4
5
E:\BaiduSyncdisk\validation\javascript>vue create vue3.x-cli
? Your connection to the default yarn registry seems to be slow.
Use https://registry.npmmirror.com for faster installation? (Y/n) y
2
3
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
> Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
Manually select features
2
3
4
5
Vue CLI v5.0.8
? Please pick a preset: Default ([Vue 2] babel, eslint)
? Pick the package manager to use when installing dependencies: (Use arrow keys)
> Use Yarn
Use NPM
2
3
4
5
Vue CLI v5.0.8
✨ Creating project in E:\BaiduSyncdisk\validation\javascript\vue3.x-cli.
⚙️ Installing CLI plugins. This might take a while...
yarn install v1.22.19
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[#####################-------------------------------------------------------] 496/772
2
3
4
5
6
7
8
9
success Saved lockfile.
Done in 11.90s.
⚓ Running completion hooks...
📄 Generating README.md...
🎉 Successfully created project vue3.x-cli.
👉 Get started with the following commands:
$ cd vue3.x-cli
$ yarn serve
E:\BaiduSyncdisk\validation\javascript>cd vue3.x-cli
E:\BaiduSyncdisk\validation\javascript\vue3.x-cli>yarn serve
yarn run v1.22.19
$ vue-cli-service serve
INFO Starting development server...
DONE Compiled successfully in 4278ms
App running at:
- Local: http://localhost:8081/
- Network: http://192.168.3.82:8081/
Note that the development build is not optimized.
To create a production build, run yarn build.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 使用vite构建vue3项目
- vite无须打包,可以快速冷启动
- 轻量快速的热重载HMR
- 真正的按需编译,不再等待整个项目完全编译完成
npm init vite-app vue3-vite
cd vue3-vite
npm install
npm run dev
E:\BaiduSyncdisk\validation\javascript>cd vue3-vite
E:\BaiduSyncdisk\validation\javascript\vue3-vite>npm install
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated rollup-plugin-terser@7.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
added 299 packages in 19s
E:\BaiduSyncdisk\validation\javascript\vue3-vite>npm run dev
> vue3-vite@0.0.0 dev
> vite
[vite] Optimizable dependencies detected:
vue
Dev server running at:
> Network: http://192.168.3.82:3000/
> Local: http://localhost:3000/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查看webpack完整配置文件
为了免于破坏,vue脚手架把webpack配置文件webpack.config.js
隐藏了,想查看完整配置文件需执行 vue inspect > output.js
命令。
# webpack中用于语法检查的loader
- eslint
- jslint
- jshint
# 自动升级过期包
- 第一步:使用npm 命令查看已过期安装包
npm outdated
- 第二步:安装升级插件
npm install -g npm-check-updates
- 第三步:查看最新版本命令
ncu
- 第四步:升级低版本npm包文件
ncu -u
# 查看包所有版本号
npm view element-plus versions
# 常用插件
- uuid 自动生成id插件
- nanoid 自动生成id插件
- pubsub-js 消息发布订阅插件
- axios 异步请求插件 (其他工具:xhr 发ajax请求鼻祖,jquery,fetch 原生工具)
# 常用技术
# 自定义事件
说明:Student 和 School 都是vue组件
自定义事件注册和触发遵循的一条规则是,事件的注册和触发需要是同一个对象
- 写法一
<Student v-on:studentEvent="getStudentName"></Student>
methods: {
// ...params包裹了给方法传递的除name外的剩余全部参数
getStudentName(name, ...params) {
console.log("通过事件获取到的姓名:" + name);
},
}
2
3
4
5
6
7
- 写法二
<Student @studentEvent="getStudentName"></Student>
methods: {
// ...params包裹了给方法传递的除name外的剩余全部参数
getStudentName(name, ...params) {
console.log("通过事件获取到的姓名:" + name);
},
}
2
3
4
5
6
7
- 写法三
在一个地方注册事件,并回调函数
<Student ref="student"></Student>
mounted() {
setTimeout(() => {
this.$refs.student.$on("studentEvent", this.getStudentName);
}, 3000);
},
methods: {
getStudentName(name, ...params) {
console.log("通过事件获取到的姓名:" + name);
},
},
在另一个地方触发事件并传递数据
<button @click="sendStudentName">把学生的姓名给app</button>
methods: {
sendStudentName() {
this.$emit("studentEvent", this.name,参数二,参数三);
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
这种写法相当于给组件自身上绑定一个原生点击事件,这样会把click事件交给Student组件最外层的div来执行
<Student ref="student" @click.native="show"></Student>
show(){
console.log('执行了一个原生dom事件')
}
如果这样写就会被当做一个自定义事件,需要通过emit来触发
<Student ref="student" @click="show"></Student>
show(){
console.log('执行了一个原生dom事件')
}
this.$emit("click");
2
3
4
5
6
7
8
9
10
11
12
解绑事件一个事件或者多个事件
beforeDestroy() {
// 解绑一个事件直接在对象上指定
this.$refs.student.$off("studentEvent", this.getStudentName);
// 解绑多个事件直接在vm上删除多个事件对象
// this.$off(['studentEvent1','studentEvent2'])
},
组件销毁时,其身上绑定的事件也将自动失效
methods: {
destroyStudent() {
this.$destroy();
},
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# http请求跨域
举个发送请求的栗子
请求端 http:localhost:8080 服务端 http:localhost:5000
解释跨域本质:浏览器发送的请求确实将请求发送到服务器上了,服务器也收到了本次的请求,服务器也把数据交给了浏览器,但是浏览器没有进一步给到用户页面(因为浏览器发现跨域请求了,所以将数据握在手里不给页面了)
解决跨域问题: 1.标准解决方式:cors,不用浏览器做任何事情,但需要服务端人员返回数据时带上必要的特殊相应头。但是有个问题就是任何人都可以找这台服务器要数据了。 2.jsonp解决方式:前后端都需要写东西才能解决。只能解决get请求方式。 3.代理服务器解决方式:代理服务的端口和浏览器访问的端口是一致的。 代理服务器开启的方式有:1.经典方式nginx 2.vue-cli
配置代理服务器,5000是存储数据的服务器端口号 //代理方式二:配置多个代理 devServer: { proxy: { // '/xxx' 请求前缀,目的是为了判断请求是否要走代理,增加了请求的灵活性 '/atguigu': { // 访问的真实数据服务器地址和端口 target: 'http://localhost:5000', // 配置路径,目的匹配服务器接口路径 pathRewrite: { "^/atguigu": "" }, // 支持websocket,默认true ws: true, // 指出请求的来源地址,true就实话实说,false就虚报地址,默认true changeOrigin: true, // 控制请求头中host值,设置成false比较好 }, '/demo': { // 访问的真实数据服务器地址和端口 target: 'http://localhost:5001', // 配置路径,目的匹配服务器接口路径 pathRewrite: { "^/demo": "" }, // 支持websocket,默认true ws: true, // 指出请求的来源地址,true就实话实说,false就虚报地址,默认true changeOrigin: true, // 控制请求头中host值,设置成false比较好 }, } }
注意:并不是所有的请求都通过代理服务器发送给5000。当8080服务器有的东西就不需要通过代理。
# 关键技术点
大总管
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.config.errorHandler = (err) => {
//定义一个应用级的错误处理器,用来捕获所有子组件上的错误。
}
app.config.warnHandler = (msg, instance, trace) => {
// `trace` 是组件层级结构的追踪,警告仅会在开发阶段显示,因此在生产环境中,这条配置将被忽略
}
app.config.performance //设置此项为 true 可以在浏览器开发工具的“性能/时间线”页中启用对组件初始化、编译、渲染和修补的性能表现追踪。仅在开发模式和支持 performance.mark API 的浏览器中工作。
app.component('TodoDeleteButton', TodoDeleteButton)//这使得 TodoDeleteButton 在应用的任何地方都是可用的。
app.mount('#app')//确保在挂载应用实例之前完成所有应用配置!//或者
app.mount(document.body.firstChild)//也可以将app挂在到实际的dom中。
app.unmount()//卸载一个已挂载的应用实例。卸载一个应用会触发该应用组件树内所有组件的卸载生命周期钩子。
app.use()//安装一个插件。1.插件可以是一个带 install() 方法的对象,亦或直接是一个将被用作 install() 方法的函数。2.插件选项 (app.use() 的第二个参数) 将会传递给插件的 install() 方法。3.若 app.use() 对同一个插件多次调用,该插件只会被安装一次。
app.mixin()//vue3不推荐使用,勉强能用完全是为了兼容性。
app.version//提供当前应用所使用的 Vue 版本号。这在插件中很有用,因为可能需要根据不同的 Vue 版本执行不同的逻辑。
setup()//setup() 钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:1.需要在非单文件组件中使用组合式 API 时。2.需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。对于结合单文件组件使用的组合式 API,推荐通过 <script setup> 以获得更加简洁及符合人体工程学的语法。
ref()//接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。
computed()//接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。
reactive()//返回一个对象的响应式代理。
readonly()//接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
isRef()//检查某个值是否为 ref。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
app.component()
如果同时传递一个组件名字符串及其定义,则注册一个全局组件;如果只传递一个名字,则会返回用该名字注册的组件 (如果存在的话)。
app.component()
例如:
import { createApp } from 'vue'
const app = createApp({})
// 注册一个选项对象
app.component('my-component', {
/* ... */
})
// 得到一个已注册的组件
const MyComponent = app.component('my-component')
2
3
4
5
6
7
8
9
10
11
12
13
14
app.directive()
如果同时传递一个名字和一个指令定义,则注册一个全局指令;如果只传递一个名字,则会返回用该名字注册的指令 (如果存在的话)。
app.directive()
例如:
import { createApp } from 'vue'
const app = createApp({
/* ... */
})
// 注册(对象形式的指令)
app.directive('my-directive', {
/* 自定义指令钩子 */
})
// 注册(函数形式的指令)
app.directive('my-directive', () => {
/* ... */
})
// 得到一个已注册的指令
const myDirective = app.directive('my-directive')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
app.provide()
提供一个值,可以在应用中的所有后代组件中注入使用。
app.provide()
import { createApp } from 'vue'
const app = createApp(/* ... */)
app.provide('message', 'hello')
//在某个应用组件中
import { inject } from 'vue'
export default {
setup() {
console.log(inject('message')) // 'hello'
}
}
2
3
4
5
6
7
8
9
10
11
12
13
app.runWithContext()
使用当前应用作为注入上下文执行回调函数。
import { inject } from 'vue'
app.provide('id', 1)
const injected = app.runWithContext(() => {
return inject('id')
})
console.log(injected) // 1
2
3
4
5
6
7
8
9
需要一个回调函数并立即运行该回调。在回调同步调用期间,即使没有当前活动的组件实例,inject() 调用也可以从当前应用提供的值中查找注入。回调的返回值也将被返回。
app.config.globalProperties
一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。
app.config.globalProperties.msg = 'hello'
这使得 msg 在应用的任意组件模板上都可用,并且也可以通过任意组件实例的 this 访问到
nextTick()
等待下一次 DOM 更新刷新的工具方法。 当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。 这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。 nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
</script>
<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
setup()
钩子是在组件中使用组合式 API 的入口
export default {
setup(props, context) {
// props 是响应式的,并且会在传入新的 props 时同步更新,请注意如果你解构了 props 对象,解构出的变量将会丢失响应性。
// 该上下文对象context是非响应式的,可以安全地解构 setup(props, { attrs, slots, emit, expose })
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
//attrs 和 slots 都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.x 或 slots.x 的形式使用其中的属性。
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
// 让组件实例处于 “关闭状态”
// 即不向父组件暴露任何东西
context.expose()
const publicCount = ref(0)
const privateCount = ref(0)
// 有选择地暴露局部状态
context.expose({ count: publicCount })
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37