vite
尤大在 Vue 3.0 beta 直播中推荐了 vite 的工具,强调:针对Vue单页面组件的无打包开发服务器,可以直接在浏览器运行请求的 vue 文件
很新颖,这篇博客用它来搭建一个 vue3 的项目试试
Vite 是面向现代浏览器,基于原生模块系统 ESModule 实现了按需编译的 Web 开发构建工具。在生产环境下基于 Rollup 打包
- 快速冷启动服务器
- 即时热模块更换(HMR)
- 真正的按需编译
node >= 10.16.0
搭建
使用 vite 搭建项目
npm init vite-app <project-name>
安装 typescript、vue-router@next、axios、eslint-plugin-vue、sass 等相关插件
配置
vite.config.ts
vite.config.ts 相当于 @vue-cli 项目中的 vue.config.js
我这简单配置一下:
import path from 'path' module.exports = { alias: { '/@/': path.resolve(__dirname, './src') }, optimizeDeps: { include: ['lodash'] }, proxy: {} }
Router
在 src 下新建 router 文件夹,并在文件夹内创建 index.ts
import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', name: 'Home', component: () => import('/@/views/Home.vue') }, { path: '/lifeCycle', name: 'lifeCycle', component: () => import('/@/views/LifeCycle.vue') } ] export default createRouter({ history: createWebHistory('/krry/'), routes })
ts types
项目根目录下新建 tsconfig.json 写入相关配置
{ "compilerOptions": { ...// 其他配置 "paths": { "/@/*": [ "src/*" ] }, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/types/images.d.ts", "tests/**/*.ts", "tests/**/*.tsx" ], "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
import { createApp } from 'vue' import router from '/@/router' import App from '/@/App.vue' const app = createApp(App) app.use(router) app.mount('#app')
然后就可以快乐地写代码了
vue3 知识
setup
vue3 中用 setup 函数整合了所有的 api;只执行一次,在生命周期函数前执行,所以在 setup 函数中拿不到当前实例 this,不能用 this 来调用 vue2 写法中定义的方法
它将接受两个参数:props、context
// props - 组件接受到的属性 context - 上下文 setup(props, context) { return { // 要绑定的数据和方法 } }
props
setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新
但是,因为 props 是响应式的,不能使用 ES6 解构,因为它会消除 prop 的响应性
如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成此操作
import { toRefs } from 'vue' setup(props) { const { title } = toRefs(props) console.log(title.value) }
context
context 暴露三个组件的 property:{ attrs, slots, emit }
它是一个普通的 JavaScript 对象,不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构
生命周期
通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子
因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们
换句话说,在这两个钩子中编写的任何代码都应该直接在 setup 函数中编写
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 中使用
使用如下:
<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
// 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
响应式地跟踪函数中引用的响应式数据,当响应式数据改变时,会重新执行函数
const count = ref(0) // 当 count 的值被修改时,会执行回调 const stop = watchEffect(() => console.log(count.value)) // 停止监听 stop()
还可以停止监听,watchEffect 返回一个函数,执行后可以停止监听
与 vue2 一样:
const unwatch = this.$watch('say', curVal => {}) // 停止监听 unwatch()
useRoute、useRouter
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 中状态修改后才能在页面中响应
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 } }