Vue3
1.创建vue3
1.1 使用vue-cli创建
vue ui
或者 vue create project
// Vue.config.js 配置选项
module.exports = {
// 选项
// 基本路径
publicPath: "./",
// 构建时的输出目录
outputDir: "dist",
// 放置静态资源的目录
assetsDir: "static",
// html 的输出路径
indexPath: "index.html",
//文件名哈希
filenameHashing: true,
//用于多页配置,默认是 undefined
pages: {
index: {
// page 的入口文件
entry: 'src/index/main.js',
// 模板文件
template: 'public/index.html',
// 在 dist/index.html 的输出文件
filename: 'index.html',
// 当使用页面 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板文件默认是 `public/subpage.html`
// 如果不存在,就回退到 `public/index.html`。
// 输出文件默认是 `subpage.html`。
subpage: 'src/subpage/main.js'
},
// 是否在保存的时候使用 `eslint-loader` 进行检查。
lintOnSave: true,
// 是否使用带有浏览器内编译器的完整构建版本
runtimeCompiler: false,
// babel-loader 默认会跳过 node_modules 依赖。
transpileDependencies: [ /* string or regex */ ],
// 是否为生产环境构建生成 source map?
productionSourceMap: true,
// 设置生成的 HTML 中 <link rel="stylesheet"> 和 <script> 标签的 crossorigin 属性。
crossorigin: "",
// 在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标签上启用 Subresource Integrity (SRI)。
integrity: false,
// 调整内部的 webpack 配置
configureWebpack: () => {}, //(Object | Function)
chainWebpack: () => {},
// 配置 webpack-dev-server 行为。
devServer: {
open: process.platform === 'darwin', //配置自动启动浏览器
host: '0.0.0.0',
port: 8080,
https: false,
hotOnly: false // 热更新
// 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/cli-service.md#配置代理
proxy: {
'/api': {
target: "http://app.rmsdmedia.com",
changeOrigin: true,
secure: false,
pathRewrite: {
"^/api": ""
}
},
'/foo': {
target: '<other_url>'
}
}, // string | Object
before: app => {}
},
// CSS 相关选项
css: {
// 将组件内的 CSS 提取到一个单独的 CSS 文件 (只用在生产环境中)
// 也可以是一个传递给 `extract-text-webpack-plugin` 的选项对象
extract: true,
// 是否开启 CSS source map?
sourceMap: false,
// 为预处理器的 loader 传递自定义选项。比如传递给
// Css-loader 时,使用 `{ Css: { ... } }`。
loaderOptions: {
css: {
// 这里的选项会传递给 css-loader
},
postcss: {
// 这里的选项会传递给 postcss-loader
}
},
// 为所有的 CSS 及其预处理文件开启 CSS Modules。
// 这个选项不会影响 `*.vue` 文件。
modules: false
},
// 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader`
// 在多核机器下会默认开启。
parallel: require('os').cpus().length > 1,
// PWA 插件的选项。
// 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md
pwa: {},
// 三方插件的选项
pluginOptions: {
// ...
}
}
1.2 使用vite创建(node版本>12.0.0.0)
兼容注意:安装Volar插件 ,使用vscode
安装vite
npm init vite@latest
或者
npm init @vitejs/app
//安装vite同时创建vite项目
npm init vite@latest my-vue-app --template vue
vite.config.js配置
//vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import {loadEnv}from 'vite'
import path from "path";
export default defineConfig({
plugins: [vue()],
base: "./", // 类似publicPath,'./'避免打包访问后空白页面,要加上,不然线上也访问不了
mode:"",//设置开发模式还是生成模式
server: {
https: false, // 是否开启 https
open: false, // 是否自动在浏览器打开
host: "127.0.0.1",//域名
port: 3000, // 端口号
proxy: {
"/api": {
target: "", // 后台接口
changeOrigin: true,//跨域处理
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, //websocket支持
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
resolve: {
alias: {
// 如果报错__dirname找不到,需要安装node,执行npm install @types/node --save-dev
"@": path.resolve(__dirname, "src"),
"@assets": path.resolve(__dirname, "src/assets"),
"@components": path.resolve(__dirname, "src/components"),
"@images": path.resolve(__dirname, "src/assets/images"),
"@views": path.resolve(__dirname, "src/views"),
"@store": path.resolve(__dirname, "src/store"),
},
},
/*
build: {
outDir: "dist",
// 9月更新
assetsDir: "assets", //指定静态资源存放路径
sourcemap: false, //是否构建source map 文件
terserOptions: {
// 生产环境移除console
compress: {
drop_console: true,
drop_debugger: true,
},
},
},
*/
},
// 引入第三方的配置
optimizeDeps: {
//include: ["element-plus/lib/locale/lang/zh-cn"],//举例
},
});
2.setup函数
组件中所用到的:数据 方法等,均需配置在setup中
1.返回一个对象,则对象中的属性,方法,在模板中可以直接使用(常用)
2.返回一个渲染函数,则可以自定义渲染内容
//注意点:
setup不能是一个async函数,因为返回值不再是return的对象,而是proise,模板中看不到reutrn对象中的熟悉。(注意:后期也可以返回一个promise实例,但是需要Suspense与异步组件(defineAsyncComponents)的配合)
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @clcik="sayHello">sayHello</button>
</template>
<script>
setup(){
let name='zs',
let age=18
function sayHello(){
alert(`我叫${name},我${age}岁了!`)
}
return { //返回一个对象
name,
age,
sayHello
}
}
</script>
3.ref函数(修改数据需要.value)
//引入ref函数
import { ref } from "vue";
setup() {
let name = ref("zs"); //响应式数据 ref()
let job=ref({
type:"前端工程师",
salary:"30k"
})
function sayhi() {
alert("zs");
}
function changeInfo() {
name.value='ls',
job.value.type="java工程师", //修改属性值 需要.value(因为是一个引用实现对象 基本数据类型使用的ref(数据劫持), 对象类型求助了vue内部的reactive函数(proxy)
job.value.salary="40k"
console.log(name);
}
return {
name,
job,
sayhi,
changeInfo,
};
},
4.reactive函数(对象类型数据)
import {reactive} from 'vue'
setup(){
// const 对象=reactive({}) 返回一个proxy的代理对象
let person =reactive({ //使用reactive函数可以是对象形式的数据变成响应式
name:'zs',
age:18,
job:{
type:'前端开发',
salary:"30k",
}
})
let hobby=reactive(['抽烟','喝酒','打麻将'])
function changeInfo(){
person.name='ls',
person.age=30,
person.job.type='后端开发',
person.job.salary='40k'
hobby[0]='学习'
}
return {
person, //返回一个代理对象(proxy代理的实例对象)
changeInfo
}
}
5.setup参数
props传参
<script>
export default{
props:['msg'],
setup(props){
console.log(props)
}
}
</script>
context参数
//父组件
<template>
<home @hello='showx' msg='你好' school='家里蹲大学'></home>
<template #qwe> //具名插槽 #qwe可以写成v-slot:qwe
<h3>你好啊哥哥们</h3>
</template>
</template>
<script>
import home from './components/home.vue'
export default {
components:{
home
},
setup(){
function showx(value){
console.log('99');
alert(`你好,你触发了hello事件,我收到的参数是${value}`)
}
return{
showx
}
}
};
</script>
//子组件
<template>
<button @click="handclick">测试触发一下父组件的hello事件</button>
<slot name="qwe"></slot> //具名插槽使用
</template>
<script>
export default{
props:['msg'],
emits:['hello'], //需要通知一下,否则会有警告(不影响使用)
setup(context){
console.log(context)
// 触发父组件
function handclick() {
context.emit('hello',999)
}
return {
handclick,
};
}
}
</script>
6.computed计算属性
<template>
<div>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.firstName">
<br>
名:<input type="text" v-model="person.lastName">
<br>
<span>全名:{{person.fullName}}</span>
</div>
</template>
<script>
import { reactive ,computed} from "vue"; //引入计算属性computed
export default {
name: "test",
// computed:{ //vue2写法(简写
// // 计算属性可以直接用
// fullName(){
// return this.person.firstName+'-'+this.person.lastName
// }
// },
setup() {
let person = reactive({
firstName: "张",
lastName: "三",
})
//计算属性--简写(未考虑可修改)
person.fullName=computed(()=>{
return person.firstName+'-'+person.lastName
})
//计算属性--完整写法(可读可改)
person.fullName=computed({
get(){
return person.firstName+'-'+person.lastName
}
set(value){
const nameArr=value.split('-')
person.firstName=nameArr[0]
person.lastName=nameArr[1]
}
})
return {
person,
//fullName
};
},
};
</script>
7.watch监听
//与vue2的watch配置功能一致
两个坑:
监听reactive的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)
监听reactive定义的响应式数据中某个属性时:deep配置有效
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我加1</button>
<h2>当前的信息为:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="person.name+='~'">修改信息</button>
<button @click="person.age++">修改信息</button>
</template>
<script>
//引入watch监听函数
import { ref,reactive,watch} from "vue";
export default {
setup() {
let sum=ref(0)
let msg=ref('你好啊')
let person=reactive({
name:'zs',
age:30
})
// 1.监视基本类型数据-多个
watch([sum,msg],(newVlaue,oldValue)=>{ //三个参数,第一个监听对象,第二个回调函数,第三个配置项如{immediate:true,deep:true}
console.log('sum或msg变了',newVlaue,oldValue);
},{immediate:true})
// 2.监听基本类型数据-单个
watch(msg,(newVlaue,oldValue)=>{
console.log('msg变了',newVlaue,oldValue);
})
// 3.监听reactive所定义的对象类型数据
watch(person,(newVlaue,oldValue)=>{
console.log('person变了',newVlaue,oldValue);//oldValue值也变了,目前是vue3中的bug
})
// 4. 监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.age,(newVlaue,oldValue)=>{
console.log('person.age变化了',newVlaue,oldValue);
})
// 5. 监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.age,()=>person.name],(newVlaue,oldValue)=>{
console.log('person.age或者name变化了',newVlaue,oldValue);
})
//特殊情况:监听reactive所定义的对象数据中的某个对象或者更深层的数据时
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person.job变化了',newValue,oldValue)
},{deep:true})//需要开启深度监听
// 返回一个对象(常用)
return {
sum,
msg,
person
}
},
};
</script>
8.watchEffect监听(一进来就触发)
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我加1</button>
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script>
import { ref, reactive,watchEffect } from "vue";
export default {
setup() {
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name: '张三',
age: 19,
job: {
j1: {
salary: 30
}
}
})
//不指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性
watchEffect(() => {
const x1 = sum.value //监视sum的值
const x2=person.job.j1.salary //监视salary
console.log('watchEffect监听了');
})
// 返回一个对象(常用)
return {
sum,
msg,
person
}
},
};
</script>
9.生命周期钩子函数
//两种方式:
第一种与vue2一样,作为配置项来写(beforeDestroy-->beforeUnmount destroyed-->unmounted)
第二种写在setup中(组合式api形式书写)
beforeCreate==>setup()
created==>setup()
其余的钩子全部带on前缀
写法: onMounted(()=>{})
10.hook
本质上是一个函数,把setup函数中使用得组合式api进行了封装
类似于vue2中得mixin
使用:
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我加1</button>
<hr>
<h2>当前点击时鼠标得坐标为:x:{{point.x}} y:{{point.y}}</h2>
</template>
<script>
import { ref } from "vue";
import usePoint from '@/hooks/usePoint' //引入hooks中封装的函数
export default {
setup() {
let sum = ref(0);
let point =usePoint() //调用hooks中得函数
console.log(point);
// 返回一个对象(常用)
return {
sum,
point
};
},
};
</script>
//在src下创建hook文件夹 -》创建usePoint.js(封装hooks函数
import { reactive,onMounted,onBeforeUnmount } from "vue"
export default function savePoint() {
//实现鼠标“打点”相关得数据
let point = reactive({
x: 0,
y: 0
})
//实现鼠标“打点”相关得方法
function savePoint(e) {
console.log(e.pageX, e.pageY)
point.x = e.pageX
point.y = e.pageY
}
//实现鼠标“打点”相关得钩子
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
11.toRef
<template>
<h3>{{name2}}</h3>
<h3>{{salary}}</h3>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script>
import { reactive,toRef} from "vue";
export default {
setup() {
let person = reactive({
name: '张三',
age: 19,
job: {
j1: {
salary: 30
}
}
})
// 返回一个对象(常用)
return {
person,
name2:toRef(person,'name')//使用toRef指向person对象(类似于深拷贝
salary:toRef(person.job.j1,'salary')
}
}}
</script>
12.toRefs
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">增长薪资</button>
</template>
<script>
import { reactive,toRefs} from "vue";
export default {
setup() {
let person = reactive({
name: '张三',
age: 19,
job: {
j1: {
salary: 30
}
}
})
// 返回一个对象(常用)
return {
person,
...toRefs(person) //使用toRefs可以得到整个对象(类似于深拷贝数据
}
}}
</script>
13.Router路由跳转与传参
方式1
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
function goto(){
router.push("/about");
}
return{
goto //一定要要放在return里才能在模板上面使用
}
}
}
方式2
import router from "../../router/index.js";
router.push("/");
路由传参
import router from "../../router/index";
router.push({path:'/addShop',query:{id:id}})
//在其他页面获取这个传参
router.currentRoute._rawValue.query.id
14.在vuex中的用法
import { useStore } from 'vuex'
export default defineComponent({
setup() {
const store = useStore()
const count = store.state.count;
return {
count,
}
}
})
其他组合式api
1.shallowReactive与shallowRef
shalolowReactive 只考虑对象的第一层数据(浅响应式)
shallowRef 只能处理基本类型数据响应式
2.readyonly与shallowReadyonly
//禁止修改数据
readyonly(深只读) shallowReadyonly(浅只读)
<template>
<h1>{{sum}}</h1>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="sum++">sum++</button>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">增长薪资</button>
</template>
<script>
import {ref, reactive,toRefs,readonly,shallowReadonly} from "vue";
export default {
setup() {
let sum=ref(0)
let person = reactive({
name: '张三',
age: 19,
job: {
j1: {
salary: 30
}
}
})
// person=readonly(person)
person=shallowReadonly(person)
// sum=readonly(sum)
sum=shallowReadonly(sum)
// 返回一个对象(常用)
return {
sum,
...toRefs(person)
}
}
}
</script>
应用场景:接收别人组件传过来的值,使用readyonly进行保护,操作结果来的值不会影响别人页面
3.toRaw与markRaw
toRaw将reactive生成的响应式数据对象还原成普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的操作不会引起页面更新
markRaw:标记一个对象,使其永远不会再成为响应式对象(常用)
应用场景:1.有些之不应被设置为响应式的,例如复杂的第三方类库等
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
4.provide与inject
使用:
//祖组件
let car=reactive({
name:'奔驰',
price:'40w'
})
provide('car',car)
//后代组件
let car= inject("car");
return{car}//记得需要return出去
响应式数据判断
isRef:检查一个值是否为一个ref对象
isReactive:检查一个对象是否由reactive创建的响应式代理
isReadonly:检查一个对象是否由readyonly创建的只读代理
isPoxy:检查一个对象是否由reactive或者readonly方法创建的代理
Teleport(传送)
传送,可以打破多级组件,直接定位到目标结构
<teleport to="移动位置"> // 将该结构直接移动到对应得结构中,比如body,htl等
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
</div>
</div>
</teleport>
Suspense与异步组件
Suspense :等待异步组件时渲染一些额外的内容,增强用户体验
//异步引入组件
<template>
<div class="son">
<h1>son</h1>
<Suspense> //使用Suspense包裹组件,并配置好default和fallback
<template v-slot:default>//在该插槽中使用组件
<Child></Child>
</template>
<template v-slot:fallback>//组件加载前的结构
<h3>加载中</h3>
</template>
</Suspense>
</div>
</template>
<script>
import {defineAsyncComponent} from 'vue'
const Child =defineAsyncComponent(()=>import('./components/child.vue'))
</script>
pinia 状态管理
安装
npm i pinia@next
//src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 实例化 Vue
const app = createApp(App)
// 安装 Pinia
app.use(createPinia())
// 挂载在真实 DOM
app.mount('#app')
----------------------------------------------------------
//若在vue2中使用 需要额外安装PiniaVuePlugin
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
pinia,
})
示例
//src/store/modules/counter.ts
import {defineStore} from "pinia"
//使用前必须用difineStore()定义一个仓库
//方式一:
export const useCounterStore = defineStore("counter",{
state:()=>{
return {
count:0
}
},
actions:{
increment(){
this.count++
}
}
})
//方式二:使用类似setup()方式定义store
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
//组件中使用
<template>
<div>count:{{counter.count}}</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from "pinia";
//导入store
import { useCounterStore } from '@store/modules/counter';
const counter = useCounterStore();//接收store数据
// const {count}=counter // 注意: 直接解构会使变量失去响应式
const {count}=storeToRefs(counter)
//方式一:修改单一数据
counter.count++
//f修改多个数据 $patch批量更新
counter.$patch({count:counter.count+1})
// 方式三: $patch 一个函数,批量更新 (建议使用方式)
// 这里传了一个函数
// mainStore.$patch(state=>{
// state.count++
// })
counter.increment()
</script>