前言
注意
现阶段并不建议使用VUE3,原因如下:
- 不再兼容IE11
- 三方的生态并不完善
- 相关文档并不完善,遇到问题相对不太好解决
但是VUE3和TS搭配体验相对较好。
VUE3 官网
尤大在 Vue 3.2 发布的时候已经在微博给出了最佳实践的解决方案:
<script setup>
TS
Volar
= 真香
Volar
是个 VS Code 的插件,其最大的作用就是解决了 template 的 TS 提示问题。
注意
使用它时,要先移除
Vetur
,以避免造成冲突。
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script
语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 Typescript 声明 props 和发出事件。
- 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
- 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
详见官方文档 单文件组件
创建项目
Vite
创建项目
Vite 是一个 web 开发构建工具,由于其原生 ES 模块导入方式,可以实现闪电般的冷服务器启动。
通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目。
查看npm版本
代码语言:javascript复制npm -v
创建项目
代码语言:javascript复制# npm 6.x
npm init vite@latest vue3_demo01 --template vue
cd vue3_demo01
npm install
npm run dev
注意:
代码语言:javascript复制# npm 7 ,需要加上额外的双短横线
npm init vite@latest vue3_demo01 -- --template vue
如果报错
Error: Cannot find module ‘worker_threads’
原因是:
Vite 需要 Node.js 版本 >= 12.0.0。
切换Node版本
查看我自己的Node版本
代码语言:javascript复制node -v
所以升级Node版本即可,这里使用nvm
管理Node版本
nvm
可以通过下面的连接下载安装即可。
http://nvm.uihtm.com/
设置镜像地址
在 nvm 的安装路径下,找到 settings.txt
添加后内容如下:
代码语言:javascript复制root: D:Toolsnvm
path: D:Toolsnodejs
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
或者执行
代码语言:javascript复制nvm node_mirror https://npm.taobao.org/mirrors/node/
nvm npm_mirror https://npm.taobao.org/mirrors/npm/
切换版本
代码语言:javascript复制# 查看可用版本
nvm list
# 安装最新的12版
nvm install 12.22.6
# 切换到12.22.6
nvm use 12.22.6
node -v
添加TS/vue-router等
安装 typescript、vue-router@next、axios、eslint-plugin-vue、less等相关插件
代码语言:javascript复制npm install axios
npm install vue-router@next
npm install typescript -D
npm install less -D
vite.config.ts
vite.config.js
重命名为vite.config.ts
import { UserConfig } from 'vite'
const path = require('path')
import vue from '@vitejs/plugin-vue'
const config: UserConfig = {
plugins: [vue()],
optimizeDeps: {
include: [ 'axios' ]
},
resolve: {
alias: {
'/@': path.resolve( __dirname, './src' )
},
},
}
export default config
router
在 src 下新建 router
文件夹,并在文件夹内创建 index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('/@/views/Home.vue')
}
]
export default createRouter({
history: createWebHashHistory(),
routes
})
views
src
下添加views
文件夹
添加Home.vue
<script setup></script>
<template>
<div class="div1">
<div class="div2">码客说</div>
</div>
</template>
<style scoped lang="less">
.div1 {
.div2 {
font-size: 20px;
}
}
</style>
src
下的App.vue
中添加<router-view></router-view>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 Vite" />
<router-view></router-view>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
ts 配置
项目根目录下新建 tsconfig.json
写入相关配置
{
"compilerOptions": {
"allowJs": true,
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["vite/client"],
"paths": {
"/@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx",
"vite.config.ts"
],
"exclude": ["node_modules"]
}
src 目录下新建 types
文件夹,里面需要配置 ts 的类型
shims-vue.d.ts
declare module '*.vue' {}
images.d.ts
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
main.ts
src
下的main.js
重命名为main.ts
import { createApp } from 'vue'
import router from '/@/router'
import App from '/@/App.vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
index.html
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
主要是
代码语言:javascript复制<script type="module" src="/src/main.js"></script>
修改为
代码语言:javascript复制<script type="module" src="/src/main.ts"></script>
VUE3 知识
setup
vue3 中用 setup 函数整合了所有的 api;只执行一次,在生命周期函数前执行,所以在 setup 函数中拿不到当前实例 this,不能用 this 来调用 vue2 写法中定义的方法
它将接受两个参数:props、context
代码语言:javascript复制// props - 组件接受到的属性 context - 上下文
setup(props, context) {
return {
// 要绑定的数据和方法
}
}
props setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新 但是,因为 props 是响应式的,不能使用 ES6 解构,因为它会消除 prop 的响应性
如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成此操作
代码语言:javascript复制import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
context context 暴露三个组件的 property:{ attrs, slots, emit } 它是一个普通的 JavaScript 对象,不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构
setup
方法和以下是等效的
<script setup>
</script>
生命周期
通过在生命周期钩子前面加上on
来访问组件的生命周期钩子
因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们 换句话说,在这两个钩子中编写的任何代码都应该直接在 setup 函数中编写
代码语言:javascript复制setup() {
onMounted(() => {
console.log('组件挂载')
})
onUnmounted(() => {
console.log('组件卸载')
})
onUpdated(() => {
console.log('组件更新')
})
onBeforeUpdate(() => {
console.log('组件将要更新')
})
onActivated(() => {
console.log('keepAlive 组件 激活')
})
onDeactivated(() => {
console.log('keepAlive 组件 非激活')
})
return {}
}
ref、reactive
ref 可以将某个普通值包装成响应式数据,仅限于简单值,内部是将值包装成对象,再通过 defineProperty 来处理的 通过 ref 包装的值,取值和设置值的时候,需用通过 .value来进行设置 可以用 ref 来获取组件的引用,替代 this.$refs 的写法
reactive 对复杂数据进行响应式处理,它的返回值是一个 proxy 对象,在 setup 函数中返回时,可以用 toRefs 对 proxy 对象进行结构,方便在 template 中使用
使用如下:
代码语言:javascript复制<template>
<div>
<div>
<ul v-for="ele in eleList" :key="ele.id">
<li>{{ ele.name }}</li>
</ul>
<button @click="addEle">添加</button>
</div>
<div>
<ul v-for="ele in todoList" :key="ele.id">
<li>{{ ele.name }}</li>
</ul>
<button @click="addTodo">添加</button>
</div>
</div>
</template>
<script>
import { ref, reactive, toRefs } from 'vue'
export default {
setup() {
// ref
const eleList = ref([])
function addEle() {
let len = eleList.value.length
eleList.value.push({
id: len,
name: 'ref 自增' len
})
}
// reactive
const dataObj = reactive({
todoList: []
})
function addTodo() {
let len = dataObj.todoList.length
dataObj.todoList.push({
id: len,
name: 'reactive 自增' len
})
}
return {
eleList,
addEle,
addTodo,
...toRefs(dataObj)
}
}
}
</script>
computed、watch
代码语言:javascript复制// computed
let sum = computed(
() => dataObj.todoList.length eleList.value.length
)
console.log('setup引用computed要.value:' sum.value)
// watch
watch(
eleList,
(curVal, oldVal) => {
console.log('监听器:', curVal, oldVal)
},
{
deep: true
}
)
watchEffect
响应式地跟踪函数中引用的响应式数据,当响应式数据改变时,会重新执行函数
代码语言:javascript复制const count = ref(0)
// 当 count 的值被修改时,会执行回调
const stop = watchEffect(() => console.log(count.value))
// 停止监听
stop()
还可以停止监听,watchEffect 返回一个函数,执行后可以停止监听
与 vue2 一样:
代码语言:javascript复制const unwatch = this.$watch('say', curVal => {})
// 停止监听
unwatch()
useRoute、useRouter
代码语言:javascript复制import {useRoute, useRouter} from 'vue-router'
const route = useRoute() // 相当于 vue2 中的 this.$route
const router = useRouter() // 相当于 vue2 中的 this.$router
// route 用于获取当前路由数据
// router 用于路由跳转
vuex
使用 useStore 来获取 store 对象 从 vuex 中取值时,要注意必须使用 computed 进行包装,这样 vuex 中状态修改后才能在页面中响应
代码语言:javascript复制import {useStore} from 'vuex'
setup(){
const store = useStore() // 相当于 vue2 中的 this.$store
store.dispatch() // 通过 store 对象来 dispatch 派发异步任务
store.commit() // commit 修改 store 数据
let category = computed(() => store.state.home.currentCagegory
return { category }
}
Vue Cli
代码语言:javascript复制vue ui
安装时VUE版本选择Vue3即可
可用UI框架
https://next.antdv.com/docs/vue/getting-started-cn