赞
踩
vue3的优点
1、vue3采用渐进式开发,向下兼容
2、性能的提升:打包大小减少41%,初次渲染快55%,更新快133%,内存使用减少54%
3、Composition API集合,解决vue2组件开发问题
4、新得api加入:Teleprot瞬移组件,Suspense解决异步加载组件问题
5、更好TypeScript支持
Vue CLI 3需要 nodeJs ≥ 8.9 (官方推荐 8.11.0+,你可以使用 nvm 或 nvm-windows在同一台电脑中管理多个 Node 版本)
1、下载安装node
下载地址:https://nodejs.org/en/
2、检查是否安装成功
1、全局安装过旧版本的 vue-cli(1.x 或 2.x)要先卸载它,否则跳过此步:
npm uninstall vue-cli -g //或者 yarn global remove vue-cli
2、安装@vue/cli(Vue CLI 3的包名称由 vue-cli 改成了 @vue/cli)
1 cnpm install -g @vue/cli //yarn global add @vue/cli
3、vue -V 检查vue版本号
1、安装vue-cli
npm install -g @vue/cli
或者
yarn global add @vue/cli
查看版本
vue --version
2、npm创建项目
vue create 项目名
如:
//我这里设置了以下淘宝源
vue create vue-next-libs --registry=https://registry.npm.taobao.org
选择最后一个
勾选如下
选择3.x
下面的选择根据自己的需求来
vue ui
会默认开启一个服务
删除之前安装失败的包(第一次安装请跳过此步)
npm uninstall node-sass
安装
npm install -D sass-loader node-sass
安装style-resources-loader
样式自动引入(公用样式里面的变量不用没页引入,就可以直接用了)
vue add style-resources-loader
在vue.config.js中使用
const path = require('path');
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
// 这个是加上自己的路径,不能使用(如下:alias)中配置的别名路径
path.resolve(__dirname, './src/style/params.less')
]
}
},
......
}
进入到项目,执行下面的命令
vue add typescript
这里选择n,生成的组件就会和文档给的一样;y则组件是class类型
上面的程序运行完,typescript也就集成好啦!
<template> <div class="red"> {{ name }} <home /> </div> </template> <script lang="ts"> import { defineComponent, reactive, toRefs } from "vue"; import home from "./components/home.vue"; // data数据的接口 interface User { name: string; age: number; get(): string; } export default defineComponent({ name: "App", components: { home, }, setup() { // ref只能用第二种写法 // 实现接口的第一种方法 属性可增不可减 // let data: User = reactive({ // name: "张三", // age: 12, // get() { // return this.name; // }, // set(){} // }); // 实现接口的第二种方法 属性不可增减 // let data = reactive<User>({ // name: "张三", // age: 12, // get() { // return this.name; // }, // }); // 实现接口的第三种方法 属性可增不可减 let data = reactive({ name: "张三", age: 12, get() { return this.name; }, set() {}, }) as User; return { ...toRefs(data), }; }, }); </script> <style lang="scss"> </style>
npm install vue-router@next --save
在src目录下面建一个路由文件routes.ts
import { createRouter, createWebHistory } from 'vue-router' // 引入组件 import Home from "./components/home.vue" import Location from "./components/location.vue" const routes = [{ path: '', redirect: '/home' //路由重定向 }, { //alias路由别名,user访问的也是home页面,也可以配置多个 // 如:alias:["/c","/n"] path: '/home', alias: "/user", component: Home }, { path: '/location', component: Location }] // 配置路由 const router = createRouter({ // history: createWebHashHistory(),//哈希模式 history: createWebHistory(),//h5 History 模式 这种方式上线需要服务器配置伪静态 routes }) // 暴露路由实例对象 export default router
在main.ts中挂载路由
import { createApp } from 'vue'
import App from './App.vue'
import routes from "./routes"
const app = createApp(App)
// 挂载路由
app.use(routes)
app.mount('#app')
在App.vue中通过router-view
渲染组件
<template> 哎呀 <router-link to="/">首页</router-link> <router-link to="/location">本地</router-link> <router-view></router-view> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ name: "App", setup() {}, }); </script> <style lang="scss"> </style>
更多详情请查看https://vuex.vuejs.org/zh/installation.html
npm install vuex@next --save
store.ts
import { createStore } from 'vuex'
const store = createStore({
state: { num: 0 }
...
})
export default store
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import routes from "./routes"
import store from "./store"
const app = createApp(App)
// 挂载路由
app.use(routes)
app.use(store)
app.mount('#app')
先引入
import { mapState,mapGetters,mapMutations,mapActions} from 'vuex'
nickname(){return this.$store.state.nickname}
age(){return this.$store.state.age}
gender(){return this.$store.state.gender}
//一句代替上面三句
computed: mapState(['nickname','age','gender'])//映射哪些字段就填入哪些字段
当还有自定义计算属性时
computed: { //computed是不能传参数的
value(){
return this.val/7
},
...mapState(['nickname','age','gender'])
}
computed: { //computed是不能传参数的
valued(){
return this.value/7
},
...mapGetters(['realname','money_us'])
}
methods:{
...mapMutations(['addAge'])
//就相当于
addAge(payLoad){
this.$store.commit('addAge',payLoad)
}
}
methods:{ ...mapActions([‘getUserInfo’])}
总结一下:
① 依赖state得到新的数据,用getters(跟computed一样,只读)
② 修改state的属性值,就用mutations(同步操作)
③异步就用actions
store.ts
import { createStore, Store } from 'vuex' import { ComponentCustomProperties } from 'vue'; declare module '@vue/runtime-core' { interface State { num: number, str: string, arr: string[] } interface ComponentCustomProperties { $store: Store<State> } } const store = createStore({ state() { return { num: 12, str: 'tom', arr: [1, 2, 4], } }, mutations: { numMutation(state: any, data: number) { state.num = data; }, strMutation(state: any, data: string) { state.str = data; }, arrMutation(state: any, data: string[]) { state.arr = data; }, }, actions: { numAction({ commit }, args) { commit('numMutation', args) } }, modules: { } }) export default store
home.vue
<template> <div class="home">我是子组件home</div> -------------{{ num }} <button @click="numAction(10)">change</button> </template> <script lang="ts" > import { toRef, toRefs, defineComponent } from "vue"; import { useStore } from "vuex"; export default defineComponent({ name: "home", setup() { const store: any = useStore(); //mapState const { str, arr, obj } = toRefs(store.state); const num = toRef(store.state, "num"); //mapGetters const { getNum } = toRefs(store.getters); //mapMutations const { strMutation: [strMutation], } = store._mutations; //mapActions const { numAction: [numAction], } = store._actions; return { str, num, arr, obj, getNum, strMutation, numAction, }; }, methods: {}, }); </script> <style scoped> </style>
vue-cli升级到3之后,减少了很多的配置文件,将所有的配置项都浓缩到了vue.config.js这个文件中。
在根目录 新建一个 vue.config.js 同名文件
① 基本路径
baseUrl从 Vue CLI 3.3 起已弃用使用publicPath来替代。
在开发环境下,如果想把开发服务器架设在根路径,可以使用一个条件式的值
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/production-sub-path/' : '/'
}
② 构建输出目录(打包位置)outputDir
module.exports = {
outputDir: 'dist',
}
③ 静态资源目录 assetsDir
放置生成的静态资源 (js、css、img、fonts) 的目录
module.exports = {
assetsDir: 'assets',
}
④ eslint代码检测 有效值:ture | false | ‘error’
设置为 true 时, eslint-loader 会将 lint 错误输出为编译警告。默认情况下,警告仅仅会被输出到命令行,且不会使得编译失败
希望让 lint 错误在开发时直接显示在浏览器中,可以使用 lintOnSave: ‘error’ 。这会强制 eslint-loader 将 lint 错误输出为编译错误
module.exports = {
lintOnSave:false,//这里禁止使用eslint-loader
}
⑤ webpack-dev-server 相关配置
devServer: { open: true,//设置自动打开 port: 1880,//设置端口 proxy: { //设置代理 '/axios': { target: 'http://101.15.22.98', changeOrigin: true, secure: false, //如果是http接口,需要配置该参数 pathRewrite: { '^/axios': '' } } } } }
module.exports = { // 部署应用时的基本 URL publicPath: process.env.NODE_ENV === 'production' ? '192.168.60.110:8080' : '192.168.60.110:8080', // build时构建文件的目录 构建时传入 --no-clean 可关闭该行为 outputDir: 'dist', // build时放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录 assetsDir: '', // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。 indexPath: 'index.html', // 默认在生成的静态资源文件名中包含hash以控制缓存 filenameHashing: true, // 构建多页面应用,页面的配置 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 在每次保存时 lint 代码 (在生产构建时禁用 eslint-loader) lintOnSave: process.env.NODE_ENV !== 'production', // 是否使用包含运行时编译器的 Vue 构建版本 runtimeCompiler: false, // Babel 显式转译列表 transpileDependencies: [], // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建 productionSourceMap: true, // 设置生成的 HTML 中 <link rel="stylesheet"> 和 <script> 标签的 crossorigin 属性(注:仅影响构建时注入的标签) crossorigin: '', // 在生成的 HTML 中的 <link rel="stylesheet"> 和 <script> 标签上启用 Subresource Integrity (SRI) integrity: false, // 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中 // 如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象 configureWebpack: {}, // 对内部的 webpack 配置(比如修改、增加Loader选项)(链式操作) chainWebpack: () =>{ }, // css的处理 css: { // 当为true时,css文件名可省略 module 默认为 false modules: true, // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS // 默认生产环境下是 true,开发环境下是 false extract: false, // 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能 sourceMap: false, //向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader) loaderOptions: { css: {}, less: {} } }, // 所有 webpack-dev-server 的选项都支持 devServer: {}, // 是否为 Babel 或 TypeScript 使用 thread-loader parallel: require('os').cpus().length > 1, // 向 PWA 插件传递选项 pwa: {}, // 可以用来传递任何第三方插件选项 pluginOptions: {} }
目前小颖公司用的 vue.config.js配置内容如下:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://xxxx',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
// 定义一个混入对象 var myMixin = { created: function () { this.hello(); }, methods: { hello: function () { console.log("hello from mixin!"); }, }, }; export default { name: "HelloWorld", mixins: [myMixin], data() { return {}; }, }; //最后会打印 hello from mixin!
Teleport
是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术。
to="xxx"
可以是标签,也可以是id,即将teleport内的内容放到"xxx"的DOM里面。简单理解,在组件里面写入teleport ,to=“body”,那么teleport 内容就变成body的子节点了
<template>
<teleport to="body" class="modal" v-if="show">
<div class="modal-mask" >关闭</div>
<slot></slot>
</teleport>
</template>
<script>
import "./style.scss";
export default {
props: {
show: Boolean,
},
};
</script>
Composition API字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。
提供了以下函数:
<template> <div class="hello"> <h1>{{ msg.text }}</h1> <button @click="getMsg">获取msg</button> <h1>{{ title }}</h1> <button @click="getTitle">获取title</button> <h1>{{ name }}</h1> </div> </template> <script > import { reactive, ref, toRefs } from "vue"; export default { name: "HelloWorld", setup() { // 使用需要先引入 var title = ref("我是一个标签"); var msg = reactive({ text: "张三", }); var user = reactive({ name: "张张", }); // 获取reactive定义的数据 var getMsg = () => { console.log(msg.text); //张三 msg.text = "里斯"; console.log(msg.text); //里斯 }; // 获取ref定义的数据 var getTitle = () => { console.log(title.value); //我是一个标签 title.value = "改变了"; console.log(title.value); //改变了 }; return { title, msg, getMsg, getTitle, // ...user, 这样写,数据就不能双向绑定了 ...toRefs(user), //toRefs结构,数据就可以进行双向绑定 }; }, }; </script> <style scoped> </style>
<template> <div class="hello"> <h1>{{ name }}</h1> </div> </template> <script > import { reactive, toRefs, watchEffect } from "vue"; export default { name: "HelloWorld", setup() { // 非响应式数据 let data = reactive({ num: 12, }); // 改变num就会执行一次,另外不管num改不改变都会执行一次 watchEffect(() => { console.log(data.num); }); setInterval(() => { data.num++; }, 1000); return { ...toRefs(data), }; }, }; </script> <style scoped> </style>
<template> <div class="hello"> <h1>{{ name }}</h1> </div> </template> <script > import { reactive, toRefs, watch, ref } from "vue"; export default { name: "HelloWorld", setup() { // 非响应式数据 let data = reactive({ num: 12, }); let age = ref(1); watch(age, (v, old) => { console.log(v, old); }); watch( () => data.num, (v, old) => { console.log(v, old); } ); //监听多个值 watch([age,data.num], (v, old) => { console.log(v, old);//v, old都是数组 }); setInterval(() => { data.num++; age.value++; }, 1000); return { ...toRefs(data), age, }; }, }; </script> <style scoped> </style>
<template> <div class="hello"> <h1>{{ title }}</h1> <p>{{ newTitle }}</p> </div> </template> <script > import { ref, computed } from "vue"; export default { name: "HelloWorld", setup() { // 使用需要先引入 let title = ref("我是一个标签"); let newTitle = computed(() => { return "哈哈哈," + title.value; }); return { title, newTitle, }; }, }; </script> <style scoped> </style>
<template> <div class="hello"> <h1>{{ name }}</h1> <input type="text" v-model="name" /> </div> </template> <script > import { reactive, readonly, toRefs } from "vue"; export default { name: "HelloWorld", setup() { // 非响应式数据 let obj = reactive({ name: "张三", age: 12, }); // readonly 将响应式数据改成只读数据 obj = readonly(obj); return { ...toRefs(obj), }; }, }; </script> <style scoped> </style>
因为 setup
是围绕 beforeCreate
和 created
生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup
函数中编写。
<template> <div class="hello"></div> </template> <script > import { reactive, toRefs, ref, onMounted } from "vue"; export default { name: "HelloWorld", setup() { // 非响应式数据 let data = reactive({ num: 12, }); let age = ref(1); onMounted(() => { console.log("onMounted"); }); return { ...toRefs(data), age, }; }, }; </script> <style scoped> </style>
父组件传参和vue2是一样的,只是子组件有点不一样,下面是子组件
<template> <div class="hello"></div> </template> <script > export default { name: "HelloWorld", props: ["msg"], setup(props) { console.log(props); return {}; }, }; </script> <style scoped> </style>
对父组件可以作为多有子组件的依赖提供程序,而不管组件层次结构有多深,这个特性有两部分:父组件有一个provider
选项来提供数据,子组件有一个inject
选项来开始使用数据。
<template> <div class="red"> <home /> </div> </template> <script > import home from "./components/home.vue"; export default { name: "App", components: { home, }, // 写法一 provide: { title: "父组件的标题", }, // 写法二 provide() { return { title: "父组件的标题", that: this, }; }, data() { return { data: "data数据", }; }, }; </script> <style > </style>
子孙组件
<template> <div class="location"> 我是孙组件 <br> 拿到home组件的title:{{ title }} <pre>{{that}}</pre> </div> </template> <script > export default { name: "location", inject: ["title","that"], setup() { return {}; }, }; </script> <style scoped> </style>
父子组件的数据都是可以双向绑定的,即:子组件可以直接改变父组件的变量,父组件改变变量时,子组件也会同时改变
Provider
在setup()中使用provider时,我们首先从vue显示导入provider方法。这使我们能够调用provider时来定义每一个property。
provider函数允许通过两个参数定义:
1、name(string类型)
2、value
Inject
父组件
<template> <div class="red"> <button @click="setTitle">改变</button> <input type="text" v-model="title" /> <br /><br /> <home /> </div> </template> <script > import home from "./components/home.vue"; import { ref, provide } from "vue"; export default { name: "App", components: { home, }, setup() { let title = ref("父组件的标题"); let setTitle = () => { title.value = "改变标题"; }; provide("title", title); return { setTitle, title, }; }, }; </script> <style > </style>
子孙组件
<template> <div class="location"> 我是孙组件 <br> 拿到home组件的title:{{ title }} <input type="text" v-model="title"> </div> </template> <script > import { inject} from "vue"; export default { name: "location", setup() { let title =inject('title') return { title, }; }, }; </script> <style scoped> </style>
Async组件
export default defineComponent({
async setup() {
const result = ref("");
await setTimeout(() => {
result.value = "HI~Async";
}, 3000);
return {
result
};
}
});
使用
<template>
<div id="app">
<Suspense>
<template #default> //插槽包裹异步组件
<Async></Async>
</template>
<template #fallback>//插槽包裹渲染异步组件之前的内容
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
style-resources-loader
自动化导入1、安装
vue add style-resources-loader
如果已经有配置文件了就选n,以防更改你有的文件
2、配置
vue.config.js文件配置
const path = require('path');
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
// 这个是加上自己的路径,不能使用(如下:alias)中配置的别名路径
path.resolve(__dirname, './src/style/params.less')
]
}
},
......其他配置
}
或者(variable变量文件,如此配置就不用每个需要用到变量的页面都引入variable,可以直接使用啦)
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: ['./src/assets/styles/variable.scss']
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。