注意:Vite 需要 Node.js 版本 >= 12.0.0 !!! node -V vite官网
npm init vite@latest
//vue
//TypeScript
//cd npm install
//npm run dev
npm install vue-router@next vuex@next
初与typescrip的约定情愫:最近项目要用到vue3,所以打算使用typescrip语法!
安装:
npx tyarn add typescript @vue/cli-plugin-typescript -D
npx tsc --init
修改 tsconfig.json:
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {
// "@": ["src"],
"@/*": ["src/*"],
"@u/*": ["src/utils/*"],
"@c/*": ["src/components/*"],
"@images/*": ["src/assets/images/*"]
},
"suppressImplicitAnyIndexErrors": true,
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
将main.js 改为 main.ts:
安装cross-env
cross-env 通过js在平台设置不同的环境变量的工具
npm install --save-dev cross-env
项目根目录新建 .env.development 文件
NODE_ENV = development
VITE_BUILD_ENV = 'development'
VITE_APP_BASE_API = 'http://xxx.xxx.x.xx:xxxx/api' //测试服务器域名
项目根目录新建 .env.production 文件
VITE_ENV = production
VITE_BUILD_ENV = "production"
VITE_APP_BASE_API = "http://xx.snxxx.com" //正式服务器域名
修改package.json
"dev": "cross-env vite --mode development",
"test": "cross-env vite --mode test",
"product": "cross-env vite --mode production",
"build": "vite build",
"build:test": "cross-env vite build --mode test",
"build:product": "cross-env vite build --mode production",
//使用
process.env.NODE_ENV 环境变量(package.json 中定义的变量)
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_API;
import.meta.env.VITE_APP_BASE_API 请求地址(.env 定义的接口请求地址)
//设置代理
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path';
export default ({mode}) => {
return defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')//设置别名
}
},
server: {
port: 3000,//启动端口
open: true,
proxy: {
// 选项写法
'/api': loadEnv(mode, process.cwd()).VITE_APP_BASE_API //代理网址
},
cors: true
}
})
}
vite.config.ts
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
//会报错Dynamic require of "resolve" is not supported
//vite的版本是3.1.0,这个版本不支持持commonJS
// const {resolve} = require("path");
import resolve from "path";
import { createStyleImportPlugin } from "vite-plugin-style-import";
export default ({ mode }) =>
defineConfig({
base: "./",
build: {
terserOptions: {
// 生产环境移除console
compress: {
drop_console: true,
drop_debugger: true,
},
},
rollupOptions: {
output: {
//静态资源分类打包
chunkFileNames: "assets/js/[name]-[hash].js",
entryFileNames: "assets/js/[name]-[hash].js",
assetFileNames: "assets/static/[name]-[hash].[ext]",
manualChunks(id) {
//静态资源分拆打包
if (id.includes("node_modules")) {
return id
.toString()
.split("node_modules/")[1]
.split("/")[0]
.toString();
}
},
},
},
},
resolve: {
alias: {
// 如果报错__dirname找不到,需要安装node,执行npm install @types/node --save-dev
"@": resolve.resolve(__dirname, "src"),
"@c": resolve.resolve(__dirname, "src/components"),
"@u": resolve.resolve(__dirname, "src/utils"),
"@images": resolve.resolve(__dirname, "src/assets/images"),
"@views": resolve.resolve(__dirname, "src/views"),
"@store": resolve.resolve(__dirname, "src/store"),
},
},
server: {
open: true, // 是否自动在浏览器打开
port: 8082,
host: "10.1.70.70",
proxy: {
// "/api": {
// target: loadEnv(mode, process.cwd()).VITE_APP_BASEURL,
// changeOrigin: true,
// ws: true,
// rewrite: (resolve) => resolve.replace(/^\/api/, ""),
// },
},
},
plugins: [
vue(),
createStyleImportPlugin({
//按需引入vant
libs: [
{
libraryName: "vant",
esModule: true,
resolveStyle: (name) => {
return `/node_modules/vant/es/${name}/style`;
},
},
],
}),
],
});
配置 const path = require(“path”); 报错:
所以要把vite版本改低:
"vite": "^3.1.0",
一、双向数据绑定原理改变
二、Vue3支持碎片(Fragments)
就是说可以拥有多个跟节点,Template 支持多个根标签。vue2不支持碎片!
Vue 3 有 createApp(),而 Vue 2 的是 new Vue(),createApp(组件),new Vue({template, render})
v-model代替以前的v-model和.sync,vue3中v-model的用法
三、Composition API)
Vue2 与vue3 最大区别是vue2使用选项类型api,对比vue3合成型api。旧的选项型api在代码里分割了不同的属性:data,computed,methods等;新的合成型api能让我们使用方法来分割,相比于旧API使用属性来分组,这样代码会更加简便和整洁。
四、建立数据data
vue2是把数据放入data中,vue3就需要使用一个新的 setup() 方法,此方法在组件初始化构造得时候触发。使用一下三个步骤来简=建立反应性数据:
五、生命周期
vue2 -------------------------------- vue3
beforeCreate -> setup() -> 实例创建之前
Created -> setup() - >实例创建之后
beforeMount -> onBeforeMount -> DOM挂载前调用
mounted -> onMounted -> DOM挂载完成调用
beforeUpdate -> onBeforeUpdate -> 数据更新之前被调用
updated -> onUpdated -> 数据更新之后被调用
beforeDestroyed -> onBeforeUnmount -> 中间销毁前调用
destroyed -> onUnmounted -> 中间销毁完成调用
activated -> onActivated
deactivated -> onDeactivated
六、父子传参不同,setup()函数特性
import { ref } from 'vue'
const count = ref(0)
console.log(count.value)
setup函数只能是同步的不能是异步的。
七、实例化
八、获取props
vue2在script代码块可以直接获取props,vue3通过setup指令传递
vue2:console.log(‘props’,this.xxx)
vue3:setup(props,context){ console.log(‘props’,props) }
props属性名任意,假设为x;事件名必须为"update:x"
<Switch :value="y" @update:value="y=$event"/>
vue2中的写法
<Switch :value.sync="y"/>
vue3中的写法
<Switch v-model:value="y"/>
九、数据和方法的定义
Vue2使⽤的是选项类型API(Options API),Vue3使⽤的是合成型API(Composition API)
vue2:data() { return {}; }, methods:{ }
vue3: 数据和⽅法都定义在setup中,并统⼀进⾏return{}
十、给父组件传值emit
vue2:this.$emit()
vue3:setup(props,context){context.emit()}
十一、watchEffect
Vue3中除了watch,还引入了副作用监听函数watchEffect,用过之后我发现它和React中的useEffect很像,只不过watchEffect不需要传入依赖项。
watchEffect它会立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
computed和watch所依赖的数据必须是响应式的。Vue3引入了watchEffect,watchEffect 相当于将 watch 的依赖源和回调函数合并,当任何你有用到的响应式依赖更新时,该回调函数便会重新执行。不同于 watch的是watchEffect的回调函数会被立即执行,即({ immediate: true })。
十二、组件通信
vue2 -------------------------- vue3
父传子 -> props -> props
子传父 -> $emit -> $emit
子组访问父组件 -> $parent -> 无
父组访问子组件 -> $ref -> expose$ref
兄弟传值 -> EventBus -> mitt
注意:
props中数据流是单项的,即子组件不可改变父组件传来的值
在组合式API中,如果想在子组件中用其它变量接收props的值时需要使用toRef将props中的属性转为响应式。
十三、路由
vue2写法:
<script>
export default {
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
next()
}),
beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
next()
}),
methods:{
toPage(){
//路由跳转
this.$router.push(xxx)
}
},
created(){
//获取params
this.$route.params
//获取query
this.$route.query
}
}
</script>
vue3的beforeRouteEnter作为路由守卫的示例是因为它在setup语法糖中是无法使用的;大家都知道setup中组件实例已经创建,是能够获取到组件实例的。而beforeRouteEnter是再进入路由前触发的,此时组件还未创建,所以是无法用在setup中的;如果想在setup语法糖中使用则需要再写一个script 如下:
<script>
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
};
</script>
vue3路由写法:
<script>
import { defineComponent } from 'vue'
import { useRoute, useRouter } from 'vue-router'
export default defineComponent({
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
next()
},
beforeRouteLeave ((to, from, next)=>{//离开当前的组件,触发
next()
}),
beforeRouteLeave((to, from, next)=>{//离开当前的组件,触发
next()
}),
setup() {
const router = useRouter()
const route = useRoute()
const toPage = () => {
router.push(xxx)
}
//获取params 注意是route
route.params
//获取query
route.query
return {
toPage
}
},
});
</script>
十四、context.emit
新增context.emit,与this.$emit(vue3中只能在methods里使用)作用相同
import {SetupContext } from 'vue'
setup(props: Prop, context: SetupContext) {
const toggle = () => {
context.emit('input', !props.value)
}
return {toggle}
}
十五、slot具名插槽
vue2:
//子组件:
<slot name="title">
//父组件:
<template slot="title">
<h1>哈哈哈</h1>
</template>
vue3:
```bash
//子组件:
<slot name="title">
//父组件:
<template v-slot:title>
<h1>哈哈哈</h1>
</template>
十六、vue3中ref
//单个:
<script>
import {onMounted, ref,} from 'vue';
export default {
setup() {
const headline = ref(null);
// Before the component is mounted, the value
// of the ref is `null` which is the default
// value we've specified above.
onMounted(() => {
// Logs: `Headline`
console.log(headline.value.textContent);
});
return {
// It is important to return the ref,
// otherwise it won't work.
headline,
};
},
};
</script>
<template>
<div>
<h1 ref="headline">
Headline
</h1>
<p>Lorem ipsum ...</p>
</div>
</template>
//v-for里的ref
<template>
// el当前元素,divs是存储每个元素的数组
<div v-for="(item, index) in list" :ref="el => { divs[index] = el }">
{{ item }}
</div>
</template>
<script>
import {onMounted, ref,} from 'vue';
export default {
setup() {
const divs = ref([]);
onMounted(() => {
console.log(divs.value)
});
return {
divs
};
},
};
</script>
十七、watchEffect
watchEffect用来代替生命周期里的onMounted和onUpdated,初始化页面的时候watchEffect里的代码会执行,当watchEffect里的数据有更新的时候同样会执行
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
注意watchEffect第一次运行是在组件挂载之前,如果需要访问DOM需要将我们的watchEffect放在onMounted里
onMounted(() => {
watchEffect(() => console.log(count.value))
})
十八、…