WebPack打包工具
webPack配置文件
创建一个webpack.config.js的打包配置文件
//webpack.config.js文件:
module.exports={
项目入口配置,
项目出口配置,
加载器配置,
插件配置,
开发模式配置,
其他
}
项目入口配置
//1.直接配置入口文件
module.exports = {
entry: './path/to/my/entry/file.js', //输出时默认为main.js
};
//2.配置基础目录,然后配置入口文件
module.exports = {
context: __dirname+'/app',
entry:"./test.js" //相对于context的路径
};
项目出口配置
module.exports = {
mode: "none",
context: __dirname + '/app',
// entry:'./test.js',
entry: {
a: './test.js'
},
output: {
asyncChunks: true, //创建按需加载的异步 chunk。
path:__dirname+"/dist2",//输出的目录,绝对路径,默认dist
// filename: 'bundle.js', //输出的文件名
filename: '[name]-666-[id]bundle[hash:5].js', //输出的文件名,[hash:5]为5位哈希值,[id]为打包的chunk的id,[name]为入口的属性名,缺省则为mian,这几个一定记住 vue和react的css作用域-就是这个几个设计的
// library: 'hqyj',//库名
library: {
name: 'MyLibrary', //库名
type: 'var', //配置将库暴露的方式。('var'、'module'、'assign'、'assign-properties'、'this'、'window'、'self'、'global'、'commonjs'、'commonjs2'、'commonjs-module'、'commonjs-static'、'amd'、'amd-require'、'umd'、'umd2'、'jsonp' 以及 'system')
},
// libraryTarget: 'umd',//配置如何暴露 library,优先级比library高但是:[请使用 output.library.type 代理,因为我们可能在未来放弃对 output.libraryTarget 的支持。]
auxiliaryComment: 'Test Comment', //各种模块化导出技术的统一注释(把type设置为umd)
//各种模块化导出技术的分别注释(webpack允许你的项目使用各种模块化技术 它都可以识别并打包)
// auxiliaryComment: {
// root: 'Root Comment',
// commonjs: 'CommonJS Comment',
// commonjs2: 'CommonJS2 Comment',
// amd: 'AMD Comment',
// },
clean: true, // 在生成文件之前清空 output 目录
// clean: {
// dry: true, // 小黑窗打印而不是删除应该移除的静态资源
// },
// clean: {
// keep: /ignored\/dir\//, // 保留 'ignored/dir' 下的静态资源不删
// // keep(asset) {
// // return asset.includes('ignored/dir');//同上
// // },
// },
}
};
加载器loader:当打包时,如果文件中引用了其他文件时,就对该文件进行编译,编译为浏览器能够读懂的文件(HTML,CSS,JS)
css-loader&style-loader
css-loader:解析css文件,将css文件引入到js文件中
style-loader:将js文件中引入的css文件挂载到HTML页面的head中
npm install css-loader style-loader --save-dev
module:{
rules:[
{test:/\.css$/,use:["style-loader","css-loader"]}
//use数组的加载是从后往前加载;test:正则表达式,以CSS结尾的文件
]
}
外部引入CSS,单独形成一个css文件,进行引入
npm i mini-css-extract-plugin --save-dev
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module: {
rules: [
//运行bundle时直接以style标签形式动态写入到页面
// {test:/\.css$/,use:["style-loader","css-loader"]},
{
test: /\.css$/,
//css生成到css文件中,项目中以<link>形式引入使用
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[hash].css' //css生成到css文件中,项目中手动以<link>形式引入使用
})
]
file-loader:引入了常见类型(好多类型, 比如视频音频图片等等媒体编码)的文件,可以使用file-loader将其打包
npm i file-loader --save-dev
module: {
rules: [
//直接以style标签形式动态写入到页面
{test:/\.css$/,use:["style-loader","css-loader"]},
//项目中引入了以下结尾的文件就会启动file-loader
{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
]
,
ts-loader:将TS编译为js
npm install ts-loader webpack typescript --save-dev
项目中需要新建一个tsconfig.json文件(这是ts编译需要的配置文件)
module: {
rules: [
//直接以style标签形式动态写入到页面
// {test:/\.css$/,use:["style-loader","css-loader"]},
{
test: /\.css$/,
//css生成到css文件中,项目中以<link>形式引入使用
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
{ test: /\.ts$/, use: 'ts-loader' },
]
},
babel-loader:将项目中的高级语言转为低版本的js,例如:TS=>js,es6=>js
npm install babel-loader @babel/core @babel/preset-env webpack -D
module: {
rules: [
//直接以style标签形式动态写入到页面
// {test:/\.css$/,use:["style-loader","css-loader"]},
{
test: /\.css$/,
//css生成到css文件中,项目中以<link>形式引入使用
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{test:/\.(png|jpg|jpeg|mp4|mp3)$/,use:["file-loader"]},
{ test: /\.ts$/, use: 'ts-loader' },
//exclude:/node_module/ 意思是下载的模块不转码,其他js文件转码
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-object-rest-spread']
}
}
}
]
},
sass-loader
npm install sass-loader sass webpack --save-dev
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
'style-loader', //不用单独引入 直接运行js代码 动态写入页面
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
],
},
插件plugin
插件的引入,通过require(),然后把插件添加到 plugins
数组中
EslintWebpackPlugin:语言查错工具
eslint-webpack-plugin插件就是使用 eslint
来查找和修复 JavaScript 代码中的问题。
eslint-webpack-plugin 3.0 仅支持 webpack 5。对于 webpack 4 请查看 2.x 分支。
环境准备:
npm install eslint-webpack-plugin --save-dev
如果未安装 eslint >= 7
,你还需先通过 npm 安装:
npm install eslint --save-dev
插件配置:
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [new ESLintPlugin(options)],
// ...
};
//options的参数配置:
context:__dirname+"/src" //指定文件根目录
extensions:String|Array[String] //指定需要检查的扩展名.不配置默认为"js"
exclude:"node_modules" //指定需要排除的文件及目录。必须是相对于 options.context 的相对路径。
files:String|Array[String] //指定目录、文件或 globs ,必须是相对于 options.context 的相对路径。 如果是目录则递归查找所有匹配 options.extensions 选项的文件。
//测试时在main.js中故意写个错误语法:
let a=20;
let a=90;
HtmlWebpackPlugin:简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于那些文件名中包含哈希值,并且哈希值会随着每次编译而改变的 webpack 包特别有用。你可以让该插件为你生成一个 HTML 文件,使用 lodash 模板提供模板,或者使用你自己的 loader。
npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: 'index.js',
output: {
path:__dirname+'/dist',
filename: 'bundle.js',
},
plugins: [new HtmlWebpackPlugin()],
};
//该插件将为你生成一个HTML5 文件, 在 body 中使用 `script` 标签引入你所有 webpack 生成的bundle.js。
//但是每次生成的模板都是新的
可以在参数中进行配置:https://github.com/jantimon/html-webpack-plugin#options
开发环境devServer
安装:npm install --save-dev webpack webpack-dev-server webpack-cli
配置参数: https://webpack.js.org/configuration/dev-server/
配置好以后小黑窗的服务启动指令: webpack server:没有打包到磁盘中
module.exports = {
devServer: {
//告诉dev-server在服务器启动后打开浏览器。将其设置为true以打开默认浏览器。但是:如果你的电脑有防火墙或者有管理员权限的限制或者电脑很卡 可能不会自定打开浏览器
open: true, //cli中 webpack serve --open
//本地服务器监听的端口
port: 8080,
//启用热更新,直接使用webpack的热更新模块.就是改了代码自动打包后自定刷新浏览器页面:bug就是同open,记住 卡了就刷新一下不用纠结
hot: true,
//指定要使用的主机(localhost|ipv4|ipv6)。如果你想让你的服务器可以被外部访问,像这样指定:
//让你同桌访问 然后你改项目代码 他就会刷新
host: '192.168.2.60',
//启用gzip压缩
compress: true,
//代理配置,这里只是配置,不用写代理服务器的代码(配置好了它帮我们实现)
proxy: {
'/api': 'http://localhost:7001',
},
// proxy: {
// '/api': {
// target: 'http://localhost:7001',
// secure:true,//如果代理的target是https接口,需要配置它
// pathRewrite: { '^/api': '/chongxie' },//请求时重写pathname
// },
// },
}
}
cli指令:在小黑窗中直接带参数 可以进行打包配置:webpack --watch
cli指令:在小黑窗中直接带参数 可以进行打包配置:webpack server --config xxxconfig.js 打包的时候启动该配置文件
Vue
Vue引入方式
第一类引入方式
CDN引入
直接在script标签中的src填入Vue的CDN链接
webpack打包引入:通过把Vue.js文件引入到项目文件中然后打包
//vue 2.0
import Vue from './vue.js'
new Vue({
el: "#app",
data: {
msg: "hello",
age:"18"
}
})
//vue 3.0
import Vue from './vue.js'
Vue.createApp({
data() {
let msg = "hello";
return {
msg
}
}
}).mount(#app)
编辑器直接生成cdn方式:Hbuilder X直接创建vue项目普通模式
第二类引入方式
把vue的代码环境集成后,在vue的代码环境中写项目,最终才经过webpack打包生成第一类方式
自己构建vue的脚手架:用npm下载vue引入到项目中然后webpack打包
/*
1.新建项目 alipay
2.初始化配置文件:npm init -y
3.下载依赖:
npm i webpack@5.74.0 webpack-cli@4.10.0 vue-loader@17.0.0 @vue/compiler-sfc@3.2.37 webpack-dev-server@4.10.0 html-webpack-plugin@5.5.0 -D
npm i vue -S
4.webpack.config.js配置:
*/
const path = require('path');
const {
VueLoaderPlugin
} = require('vue-loader');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
watch: true,
entry: './src/main.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader',
}, ]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html'
}),
new VueLoaderPlugin(),
],
devServer: {
open: true,
port: 8080,
hot: true,
host: '192.168.2.60',
compress: true,
},
}
//5.项目配置文件 pakage.json文件中:
//scripts:{
//"dev": "webpack serve --config webpack.config.js"
//}
//6.main.js文件:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
document.body.innerHTML+="444"
//7.模板html文件中
<body>
<div id="app"></div>
<body>
使用官网脚手架的方式构建项目环境
1. npm install @vue/cli -g //下载官方脚手架
2. vue create app1 //项目名称
3. 接下来让你选择一些默认要生成的工具,不管直接回车
4. 启动:
npm run serve //生成的打包文件在内存中不会写入磁盘用于开发阶段
或者
npm run build //生成的打包文件在dist中 用于项目上线
可视化项目管理方式: vue ui
1.下载官网脚手架
2.小黑窗命令 vue ui 可以通过 vue -h查看所有的命令
3.在网页中创建项目
编辑器直接生成环境
HbuilderX 创建项目 选择 vue项目 vue-cli默认模板
然后要:npm i
再:npm run serve
禁用eslint,在vue.config文件中
module.exports = {
lintOnSave: false
}
Vue对象和基础指令
如何解决vue加载初的闪屏
1.添加一个属性 v-cloak vue运行时,会将v-cloak属性去掉,通过属性选择器隐藏标签,就没有括号闪烁了
2.利用$mount去挂载
3.使用指令v-text="" 底层xx.innerText = ""或者v-html="" => xx.innerHTML=""
v-pre 静默-跳过编译显示括号
普通插值表达式插入数据: 在标签尖括号中使用{{}}插入js表达式:变量,函数调用,三目运算等等。{{}}里放入data的属性名或者methods的方法名
body:
<div id="app">{{msg}}</div>
js:
new Vue({
el:"#app",
data:{
msg:200
}
})
给元素绑定属性
给标签绑定vue对象中的变量
标准写法:v-bind:src = “变量”
简写: :src = “变量”
方法与事件绑定
methods:Vue中的方法都写在这里
通过给元素添加@click=“方法”,给元素绑定事件
元素可以同时添加多个事件-方法调方法
面试题:DOM操作中的监听器第三个参数true还是false能阻止冒泡?都不能阻止,默认是false只会影响元素是在捕获阶段触发还是冒泡阶段触发,false冒泡阶段触发,true捕获阶段触发
事件修饰符
@click.stop 阻止冒泡
事件修饰符,多修饰符可以多连 连点没有顺序
例如 .self.stop
.stop 阻止冒泡
.self 只有当目标是自己时才会触发,当冒泡到自己时不会触发,会跳过自己冒泡,继续冒泡下一个元素。不会影响事件链
.prevent 阻止元素的默认事件
.once 只绑定了一次,绑定了一次后就会事件解绑
样式绑定
直接在元素中绑定属性 这里需要注意里面的属性是驼峰命名
:style = "{display:'none',color:'red'}"
在对象中写属性,然后对元素绑定属性
<div :style="style">666</div>
new Vue({
el: "#app",
data: {
style: {
color: "red",
fontSize: "100px"
}
})
绑定多个属性
<div :style=[style,{background:'red'}]>666</div>
new Vue({
el: "#app",
data: {
style: {
color: "red",
fontSize: "100px"
}
})
class绑定
:class = "变量" //一个变量
:class = "['自己带的class',变量]" //多个class
:class = {class:true/false} //true则有该class,false则没有该class
条件渲染
v-if:通过移除元素,true添加元素,false删除
具有更高的渲染消耗,常用在不常切换的业务
<div v-if="true">
{{msg}}
</div>
v-show:通过css渲染消失,会占用内存
具有更高的内存消耗,常用在经常切换的业务
<div v-show="true">
{{msg}}
</div>
循环渲染
v-for
v-for的元素和它的子元素都会被克隆
<div id = ""app>
<div v-for="el in arr">
{{el}}
</div>
</div>
new Vue({
el:"#app",
data:{
arr:["hello","vue","js"],
}
})
v-for与v-if连用
通过v-if的条件来限制v-for渲染
vue2.0中v-for与v-if写到一个元素中,v-for的优先级更高
1.把两者写成嵌套关系会多出来一个盒子,可以通过template标签
vue3.0不能写到一起 不然报错
v-for绑定key
key尽量绑独一无二的值
意义:多选框选择后加载更多的选择在前面出现选择的与内容不符合
1.提高绘制渲染效率
2.让数据跟真实的dom一一关联,让它不发生渲染混乱的情况
原理:
<div id="app">
<button @click="add">点击</button>
<div v-for="el in arr" :key="el.id">
<input type="checkbox" name="goods">
<span v-text="el.test"></span>
</div>
</div>
v-once:只渲染一次,后面数据源改变等都不会再修改这个
v-model:双向数据绑定
vue全家桶(vue.js vue-axios vue-router vuex element-ui)
什么是响应式数据
如果内存中的数据变化了,页面的UI也会动态跟着刷新,这种数据就是响应式数据
响应式数据设计原理
vue2.0 采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
缺陷: 对于数组来说劫持的是它的方法,通过数组的方法去修改数组的内容重新渲染页面,不能直接通过下标来进行修改。
vue3.0 使用了es6 proxy 代理了data对象
双向数据绑定
1.如果数据容器中的数据变了也会让页面刷新(dom操作让页面改变)
2.如果用户操作dom改变了页面,反之也会让数据容器中的数据的值改变
原理:v-model实际是指定其:value和@input
<div id="app">
<p>{{msg}}</p>
<input type="text" :value="msg" @input="fn">
<!-- v-model -->
<input type="text" v-model="msg">
</div>
new Vue({
el: "#app",
data: {
msg: "hello"
},
methods: {
fn(e) {
this.msg = e.target.value;
}
}
})
过滤器
filters:{}
{{msg|tool|tool2}}、{{msg|tool}}、{{msg|tool(arg1,arg2)}}
将msg作为参数传给tool过滤,再将这个结果传给tool2过滤
加小括号后会将前面的参数作为第一个参数,括号的参数依次传入
只能插值表达式({{}})和属性绑定(v-bind)使用
<div id="app">
<p>{{msg|tool}}</p>
<a :href="url|tool2">百度</a>
</div>
new Vue({
el: "#app",
data: {
msg: "2001-7-26"
},
filters: {
tool(str) {
let age = new Date().getFullYear() - new Date(str).getFullYear()
return age + '岁'
}
tool2(str) {
return "http://" + str
}
}
})
计算属性
计算属性就是处理数据源中的数据,然后用于渲染,而且会监听计算属性中使用的数据源,然后把计算的结果缓存起来
如果监听的数据源发生了变化才会重新计算,否则直接使用缓存的数据渲染
缺点:如果简单的运算也用计算属性,反而会增加资源消耗(计算属性会去监听计算的值,而且会缓存计算的结果),比如生日转年龄时,可以用过滤器
<div id="app">
<p>{{birth}}--{{age}}</p>
<p>{{x}}</p>
</div>
new Vue({
el: "#app",
data: {
birth: "2001-09-10"
},
computed: {
// 语法糖
age() {
return new Date().getFullYear() - new Date(this.birth).getFullYear()
},
// 标准写法
x: {
get() {
// 取值
return 100
},
set(data) {
// 存值
}
}
}
})
//当data中其他的数据改变时,computed里的计算属性不会重新调用,computed会监听计算的数据,只有当计算的数据发生改变时,才会重新调用
属性侦听器
必须和data中的数据源同名
如果是侦听一个对象,如果修改对象内部的属性不会触发侦听器,只有修改了引用数据的引用,才能触发侦听器属性
深度侦听:侦听对象
侦听对象,如果对象内部属性发生改变,触发侦听器
new Vue({
el: "#app",
data: {
obj: {
n:100
},
},
watch: {
obj(){
deep:"true",
handler:()=>{}
},
},
})
计算属性,属性侦听器,方法,过滤器有什么区别?那些是被this对象劫持过的?
计算属性-方法
计算属性:会把计算的结果缓存起来,并监听计算过的数据源,如果监听的数据源发生变化才会重新计算(没有监听的属性发生改变,模板会重新渲染)
方法:一般是给模板事件和其他方法使用,如果在模板中使用了方法,而且不是事件,如果模板中的数据源的任意数据改变了,它会重新调用
过滤器:往往用于数据渲染前的数据处理,除了用法跟方法不一样其他都一样,它没有被劫持
属性侦听器:只有侦听的属性发生变化才会触发(可以深度侦听,但是更消耗内存)
计算属性和方法被劫持了
自定义指令
v-name 自己定义的属性
vue 通过directives选项去接收
inserted:被绑定的元素进行DOM操作,当插入父组件时调用
<div id="app">
<p>{{n}}</p>
<input type="text" v-focus>
</div>
new Vue({
el: "#app",
data: {
n: 100,
},
directives: {
focus: {
//指令的定义
inserted(el) {
//指令的作用
el.focus()
}
},
}
})
自定义指令钩子
bind:只调用一次,指令第一次绑定时调用,进行初始化设置
inserted(el,binding):当插入父组件时调用,el表示绑定的元素,binding表示传入的对象就是绑定的数据通过binding.value取出来
update:当前组件的数据源更新后才会调用
componentUpdated:
unbind:只调用一次,与元素解绑
Vue.nextTick
在vue中引入ECharts时,需要考虑引入时是否能操作vue同时页面标签的宽高是否加载完毕,
如果ECharts的配置项(options)在vue的data中会找不到options由于inserted在生命周期created之前先调用
此时通过Vue.nextTick(fn) 在延迟回调函数将ECharts延迟调用则可以直接显示在页面中
生命周期函数
在vue实例中达到一定条件后执行的函数,就将这些函数称为周期函数
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroy
<div id="app">
<p>{{msg}}</p>
<button @click="change">修改数据源</button>
</div>
new Vue({
el: "#app",
data: {
msg: 6666
},
methods: {
change() {
this.msg = 88888
}
},
beforeCreate() {
alert("此时我还没有创建vue对象,还不能对实例对象的data操作");
// 这里还不能对vue进行操作,一般在这里进行预加载,不在这里进行对页面渲染的操作
},
created() {
alert("此时我已经创建好了vue对象,已经可以对实例对象中的data进行操作了,但是还没有对页面进行渲染");
console.log(this.msg);
},
beforeMount() {
alert("此时我已经创建好了vue对象,准备对页面进行挂载(渲染)了");
},
mounted() {
alert("此时我已经创建好了vue对象,已经完成了对页面进行挂载(渲染)了");
console.log(this.$el)//当前组件的真是DOM
},
beforeUpdate() {
console.log(this.msg);
// 第一次渲染时不会执行这个钩子函数
alert("此时数据源发生了改变,我要准备重新渲染页面了");
// 只有当渲染的数据源发生了改变后才会进行这里的操作,没有渲染的数据源改变后不会执行这里的操作
},
updated() {
alert("我已经重新渲染完成了");
},
beforeDestroy() {
alert("这个实例对象即将毁灭,将这个页面中需要保存的数据缓存起来");
},
destroyed() {
alert("啊!我死了")
},
})
组件
通过vue的components选项放组件
注册组件时,组件名不要用系统已经使用过的,组件template模板中,只能有一个根元素
组件名用驼峰命名时,使用时可以用连字符使用
一个组件中的data必须是一个函数,一个组件可以进行复用
全局:所有组件共同的功能(指令,过滤器,组件)
Vue.filter("name",function)
Vue.directive("name",function)
Vue.component("tagName",option)
局部:只有当前组件的功能(指令,过滤器,组件)
filters:{},
components:{},
directives:{}
注册组件时,使用时可以用双标签也可以用单标签:如果有插槽必须用双标签
单文件组件
在项目下创建vue.config.js就是vue的打包配置文件
@代表了src目录的文件,这里是vue的脚手架集成的basepath会去src文件夹里寻找,其他框架没有@
组件名不能跟vue中和原生DOM中的名字相同
如果.vue的script注释了,在打包的时候vue的打包环境会帮我们把这个文件解析为一个对象,然后给这个对象添加一个template属性,值为我们的<template>页面模板
<style>可以写多个
每一个组件内部只能有一个根元素,不要在根元素上写v-for
props
给子组件自定义一些属性,这个属性变成了子组件实例的属性
通过props将数据作为一个自定义的属性传递给子组件
属性的类型验证配置的方式注册组件的属性
//直接注册组件的属性
//props:['title','msg']
//配置的方式注册组件的属性
//这里配置的都是属性,通过给属性赋值传递给子组件
props:{
title:String,//这里会属性类型验证,字符串
count:[Number,String],//这里会属性类型验证数字或者字符串
color1:{
default:"red",
type:String,
},
pro1:{
validator:function(v){//自定义验证
let arr = ["success","danger"]//传入的值是否是数组中的值是就通过,否则就警告
return arr.includes(v)
}
}
}
组件中资源打包问题
项目中的本地资源打包时,webpack打包时遇到了import或者是标签内部直接写的路径时,都会启用file-loader等去加载资源,如果把图片等本地资源的路径写在data中,然后页面使用,因为webpack不会读取data所有不会打包资源
CSS作用域
全局样式
全局样式:.vue中的
样式穿透 4种方式
对环境有要求 /deep/ :deep >>> ::v-deep
在父组件的样式中写子组件的样式,可以避免选择器相同时子组件的样式被父组件的样式覆盖
/deep/ .el{}
parent :deep .el{}
parent:deep(.el){}
插槽 slot
<slot></slot> //放在子组件中,表示后面这里是插槽的槽位(会在这里插入内容)
插槽内可以包含任何模板代码,包括HTML也可以包含其他组件
插槽可以通过name名指定是哪个插槽进行渲染
如果子组件中有多个插槽都没有分配name属性,并且父组件中使用时也没有规定name则会将所有没有name属性的插槽都插入
//父组件
<template>
<div class="">
<Box :title="msg">
<div class="slot">
{{title}}
</div>
<p name:s1>hello66666666</p>
<p #s1>hello9999</p> //#s1是语法糖 标准写法v-slot:s1
<!--
* <template v-slot:s1> //将template直接传进去
<p>hello9999</p>
</template>
* -->
</Box>
<p>hello66666666</p>
</div>
</template>
<script>
import Box from '@/components/Box.vue'
export default {
data() {
return {
msg: "这是父组件的传入给子组件的数据",
title: "父组件的插槽"
}
},
components: {
Box
}
}
</script>
// ===================================================
//子组件:
<template>
<div class="box">
<slot></slot>
<!-- 如果父组件没有使用name属性则这里也会插入 -->
<p>{{title}}</p>
<slot></slot>
<!-- 如果父组件没有使用name属性则这里也会插入 -->
<slot name ='s1'></slot>
<!-- 这里只会插入name:s1或name='#s1'的内容 -->
</div>
</template>
<script>
export default {
props:['title']
}
</script>
自定义事件
v-on:xxx='fn',
v-on:click.native 表示是原生的事件,只有官方提供的事件才能加.native,自己写的加了没有作用
自己定义事件类型,触发条件由自己设计
触发组件内的事件
this.$emit("type",args)//第一个参数表示事件类型,第二个参数表示传入的值
当父组件调用子组件时,如果给父组件中给子组件绑定了官方事件,如果没有加native则不会使用,只有加了native后才会调用
//父组件:
<div>
<box1 v-on:click.native="fn"></box1> //这里如果不加native会被认为是自定义组件,必须在子组件内调用才会生效
</div>
//父组件
<div>
<box1 v-on:myclick="fn"></box1>
</div>
//子组件
<template>
<div class="box">
<button @click="x">点击一下</button> //子组件设定触发条件
<p>这里也会跟着触发?</p>//不会
</div>
x(){
this.$emit("myclick") //触发父组件给子组件绑定的事件
}
</template>
配置代理:webpack-devServer 笔记中
vue.config.js中配置
proxy: {
'/api': {
target: 'http://192.168.10.10:7001',
secure:true,//如果代理的target是https接口,需要配置它
pathRewrite: {
'^/api': '/' //这里会把请求的路径中含有/api的转为/
//例如请求:http://192.168.xx:8080/api/test/
//会变成:http://192.168.xx:8080/test/
},
//请求时重写pathname
//项目组件中请求的是http://192.168.10.10:8080/api/test
//8080服务器就会帮我们代理请求http://192.168.10.10:7001/test
},
},
baseURL
axios.defaults.baseURL='http://192.168.xx.xx:8080/api'
//配置公共的url 如果用这个axios去请求 '/ajax1',实际请求的是'http://192.168.xx.xx:8080/api/ajax1'
//main.js:
import axios from 'axios';
axios.defaults.baseURL='http://192.168.xx.xx:8080/api'//这里一般在项目打包后会注释掉
//前端页面请求axios('/test') 会自动拼接成http://192.168.xx.xx:8080/api/test
Vue.prototype.$axios = axios //将axios定义到全局去,每个组件都可以使用axios而不需要再次引入
组件中的data为什么设计成函数
1.组件复用时,使每个组件复用是独立的数据源,才不会让data数据源共用
2.函数的设计就像懒加载一样,当使用组件时,数据源的对象才会创建,这样设计性能更好
数据劫持的顺序
this组件对象有很多属性和方法都是劫持"别人"的:比如data、methods、props
this在构建时,是劫持了data>props属性>方法>计算属性>事件中给this添加的,使用的优先级:data<props属性<方法<计算属性<事件中给this添加的
初始化顺序 computed,methods,data,props
Sass、Less
vue create init时选择帮我们配置sass的加载器
需要下载一个sass预编译,在style标签添加lang="scss"就可以在标签里写sass代码了
<style lang='scss'></style>
单项数据流
数据从顶层流到底层,只能从父组件传给子组件,不能反向传值就叫做单向数据流
vue加载流程
1.每一个组件在加载时都会调用vue内部的render函数来把这个组件的tamplate选项的模板解析为一个JS对象
这个对象跟DOM节点对象"长得一模一样",就是为了后来的渲染
2.然后是数据劫持代理监听等等
底层有一种设计: 发布/订阅设计 其实就是写了一个watcher函数去订阅(监听) 数据的改变(底层js语法就是defInedproty,v3是proxy)
当数据发生变化以后:
当状态(数据)变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异(同层比较从树的两边往中间比较,再比较下层)
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了
DIFF算法
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了
组件传值
父传子
通过给子组件传属性就将数据传进去了
//父组件:
<div>
<p>父组件的msg——{{msg}} </p>
<Box :msg=msg></Box>
</div>
data() {
return {
msg: "父组件的data",
}
},
//子组件:
<div class="box">
<p>传给子组件的data——{{msg}}</p>
</div>
{
props: ['msg'] //父组件传入的属性,
}
子传父
通过自定义事件绑定后调用this.$emit(“type”,‘value’),将修改的数据传给父组件,父组件通过自定义事件修改数据
//父组件:
<div>
<p>父组件的msg——{{msg}} </p>
<Box @myclick="change" :msg=msg></Box>
</div>
data() {
return {
msg: "父组件的data",
}
},
methods: {
change(v) {
this.msg = "88888" //自定义事件触发后通过父组件修改data数据
},
}
//子组件:
<div class="box">
<p>传给子组件的data——{{msg}}</p>
<button @click="change">修改父组件data</button>
</div>
props: ['msg'],
methods: {
change() {
this.$emit("myclick", this.msg) //这里调用自定义事件去修改父组件的data,
},
}
xx.sync:将属性和事件绑在一起,子组件通过设置触发条件去修改父组件传入的数据的值,this.$emit(“update:a1”,“修改的内容”)
// 父组件:
<test :my.sync='msg'></test> //给子组件绑定自定义事件my,并传入属性my
// 子组件:
<p>传入的数据——{{my}}</p>
<button @click='fn'>修改数据</button>
props:['my'] //接收传入的属性(数据)
methods: {
fn() {
this.$emit("update:my", "子组件修改了数据");
}
}
组件间的双向数据绑定 v-model
组件通过v-model传值可以理解为:v-model也是:value,@input的一个语法糖
父组件
<Box :value="msg" @input="某个函数"></Box>
子组件
<button @click="change">chaneg1</button>
props:["value"],
methods:{
change(){
this.$emit("input","修改的内容")
}
}
给子组件传入了value属性并且定义了input事件,因此子组件需要接收value属性,并写触发input事件的条件
v-model:属性名 = '' 可以自定义传入的属性名,不一定是value
v-model.number
v-model.trim
v-model.lazy:将input事件改为change事件
实际用法:
//父组件:
<box1 v-model="msg"></box1>
data(){
return {msg:"父组件的数据"}
}
===================================================================
//子组件
<p>{{value}}</p>
<button @click="fn">change</button>
props: ['value'],
methods: {
fn() {
this.$emit("input", "子组件修改的数据")
}
}
组件多层传值
中间组件在给子组件传父组件的数据时,通过v-bind="$attrs"和v-on="$listeners",将父组件的数据全部传给子组件
中间组件不能用props去接受任何数据,否则不能传给中间组件的子组件,如果需要使用数据可以直接通过$attr.xx调用父组件传下来的数据,孙组件通过props去接收需要的数据
//根组件:
<p>父组件的msg——{{msg}} </p>
<Box :d1="msg" @x="xchange"></Box>
data() {
return {
msg: "app组件的数据"
}
},
methods:{
xchange(v){
this.msg = v;
}
}
//子组件 中间组件
<P>中间组件子组件——{{$attrs.d1}}</P> //中间组件不能通过props去接收父组件传入的数据,因为要把数据传入给子组件
<box1 v-bind="$attrs" v-on="$listeners"></box1> //这个是中间组件引入的子组件
components: {
box1,
},
//孙组件
<p>孙组件——{{d1}}</p>
<button @click="fn">孙组件</button>
props: ['d1'], //传入的属性d1
methods: {
fn() {
this.$emit("x", "孙组件修改的数据") //满足条件后调用事件
}
}
获取组件 r o o t − root- root−parent- c h i l d r e n − children- children−refs
$root: this.$root 会返回组件的根组件
$parent: this.$parent 会返回组件的父组件
$children: this.$children 返回组件的子组件对象数组,是乱序的数组,不是按照代码顺序的子组件顺序,与$refs一起用可以去操作子组件
$refs:是一个对象,属性名就是绑定在模板中的ref的值,通过组件的'id'去访问组件,组件需要添加 ref = "xxx" 类似id名一样,访问时this.$refs.xxx就可以访问到该组件了,
中间组件:
<box1 ref="mybox1"></box1> //子组件
<box1 ref="mybox" ></box1> //子组件
<button @click=fn>子组件</button>
methdos:{
fn(){
console.log(this.$children,this.$refs.mybox,this.$parent,this.$root)
// 子组件们(是一个乱序的数组对象)、id为mybox的子组件、组件的父组件、组件的根组件
}
}
依赖注入-Provide/Inject
提供者/消费者
provide:{
msg:'xxxxx'
}
provide(){
return {msg:'xxxxx'}
}
inject:["msg"]
provide-inject 父组件给子组件的值是基本数据类型,就不会是响应式的:
1.provide写成一个函数返回一个对象,将数据内存空间引用传给子组件
inject接收
2.提供基本数据,子组件接收基本数据不会刷新页面(非响应式),父组件传入一个函数这个函数需要返回这个基本数据,子组件接收后,通过计算属性去计算调用这个函数,这样当父组件修改了这个数据后,子组件也会跟着变化
根组件
data(){
return { obj:{msg:"父元素传值"}}
}
provide(){
return {
msg:(arg=this.msg) => { //根组件传函数通过函数修改数据源
this.msg = arg;//多层组件将值传进来修改数据
return this.msg;
}
}
}
子组件
inject:["msg"],
provide() {
return {
n: this.obj //父组件直接传入对象,同一个引用直接修改数据源
}
}
孙组件
<p>{{msg()}}---{{n.age}}</p>
<button @click='fn'>孙组件修改</button>
inject:["msg",'n'],
methods: {
fn() {
console.log(this.msg("孙组件修改了内容"));//这里将修改的内容传给根组件的函数去修改数据源
this.n.age = 60//可以直接修改父组件的数据源
}
}
中央事件总线 $bus
Vue提供的技术:某继承Vue的组件有三个功能
1.触发x组件的a事件:x. e m i t ( " a 事件 " , 参数 . . . ) 2. 给 x 组件绑定 a 事件 : x . emit("a事件",参数...) 2.给x组件绑定a事件:x. emit("a事件",参数...)2.给x组件绑定a事件:x.on(“a事件”,监听器函数)
3.给x组件解绑a事件:x.$off(“a事件”,监听器函数)
<button @click="fn1">绑定事件</button>
<button @click="fn2">触发事件</button>
methods: {
fn1() {
this.$on('look', function() {
console.log(this);
}) //绑定事件
},
fn2() {
this.$emit('look');//触发事件
this.$off('look')//解绑
},
}
动态组件——常用来切换组件
<component is="组件名"></component> //传组件名替我们写组件,必须要先在components里注册组件
<component :is="mytemp"></component>
// v-bind:is = 'xxx' ==> :is = 'xxx'
<button @click="fn('box2')"></button>
//<button @click="mytemp='box2'"></button>
data(){
return {
mytemp:"box1"
}
},
methods:{
fn(name){
this.mytemp = name;
//进行组件间的切换,切换组件时,之前的组件就被销毁了
}
},
beforeDestroy:{
//这里组件销毁时,将用户输入的数据保存下来
}
缓存组件 keep-alive
将组件放在<keep-alive>标签里面,就可以将组件缓,即缓存用户的操作例如用户输入的信息等等
<keep-alive include = 'box1' exinclude = 'box2' :max='2'>
<component :is="mytemp"></component>
</keep-alive>
//默认全部加载过的组件都会缓存起来
//该标签属性:
// include 需要缓存的组件 可以写组件名字符串也可以写正则,
// :include = "/box1|box2/"
// exinclude 不需要缓存的组件
// max 数字最多缓存几个,缓存最近的被渲染过的几个组件
keep-alive提供的钩子函数:写在被切换的组件内部
activated:切回该组件的时候才会执行该函数
deactivated:切其他组件的时候才会执行该函数,从一个组件切到另一个组件的时候
异步组件——懒加载组件
需要这个组件时才显示,常与动态组件连用
component:{
Box:() => ({
// 需要加载的组件 (这个 `import` 函数会返回一个 `Promise` 对象。)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
}),
}
路由
路由配置
npm i vue-router 自己下载
vue creat xxx 用脚手架创建时,选上vue-router
router/index.js:
import Vue from 'vue'
//引入路由插件
import VueRouter from 'vue-router'
//引入首页组件:一开始就会打包,因为首页一般是用户直接访问的页面
import Home from '../views/home/index.vue'
//给组件的原型链绑定功能:this.$router this.$route
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue')
}
]
//配置规则
const router = new VueRouter({
mode: 'history',
routes
})
export default router
main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
//引入路由:主要功能就是配置路由的规则(路由模式 注册的路由地址-网址 组件原型链中绑定路由对象和路由信息)
new Vue({
router,
//挂载路由到整个项目中:主要功能就是监听地址栏的改变,然后修改项目中的router-view组件应该加载的组件
render: h => h(App)
}).$mount('#app')
页面跳转
router-link
<a href="/login">go login</a><br>
//a标签会有请求服务器,然后刷新网页,因此用在链接外部网站
<router-link to={path:'/login'}>go info</router-link>
<router-link to='/login'>go info</router-link>
//它渲染到页面也是a标签但是只是改变了地址栏的网页并没有重新加载网页
//路由的底层就是采用history去监听地址栏的变化,然后把当前app组件中的router-view切换了,这种跳转用在网站内部的路由跳转
<button @click='goinfo'>js跳转</button>
methods:{
goinfo(){
this.$router.push({
path:'/login',
})
}
}
跳转路由传参
传递的参数都在路由信息对象中:路由对应的组件中获取 this.$route
1.直接在路由中拼接传的数据
<router-link to='/login?id=123&age=23'>go info</router-link>
2.通过methods方法中的跳转时,通过this.$router{path:'xxx',query:{name:'xx',age:'21'}}
接收数据
目标网址在created后通过this. r o u t e . q u e r y 获取数据 , 如果没有传参就是 u n d e f i n e d ∗ ∗ 动态传参 ∗ ∗ m e t h o d s 方法中传入参数 , 将参数传给 q u e r y , 或者拼接到网址中 t h i s . route.query 获取数据,如果没有传参就是undefined **动态传参** methods方法中传入参数,将参数传给query,或者拼接到网址中 this. route.query获取数据,如果没有传参就是undefined∗∗动态传参∗∗methods方法中传入参数,将参数传给query,或者拼接到网址中this.router.push(/login?id=${arg}$age=21
)
动态路由传参(params传参)
js:
{
path: '/login/:id',
name: 'login',//用于路由跳转
component: () => import('../views/login/index.vue')
}
<router-link to='{name:'注册的路由的name',params:{id:参数}'>go info</router-link>
this.$router.push('/login/参数')
this.$router.push({name:'login',params:{id:参数})//必须用name跳路由
路由嵌套
添加一个children属性,存放子路由信息,path中可以不添加/如果添加/的话需要加上前面父组件的路径例如:/login/xx =>login下的xx路由
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue'),
children:[
{path:'a',name:"a",component:()=>import('xxx.vue')},{path:'b',name:"b",component:()=>import('xxx.vue')},{path:'/login/b',name:"b",component:()=>import('xxx.vue')}
]
}
]
通配路由
const routes = [
...,
...,
{
path:'/xx',
component:()=>import('xxx.vue'),
redirect:'/xx/xx', //用户输入的是/xx路由,帮他重定向到/xx/xx
children:[
{
path:'*',//如果输入的是/xx/随便乱输,会重定向到/xx/xxx
component:()=>import('xxx.vue'),//乱输跳的页面
}
]
}
,{
path:'/*', //网址随便乱输,一般用来404网页
component:()=>import('xxx.vue')
}
]
路由前进后退
mode:"history"
底层选择切换组件的方式
history:底层是H5的window.history的技术,当地址栏的history状态发生变化时,切换了router-view渲染的组件,来'欺骗'用户达到切换新网页的效果,需要后端配合
hash:底层是老的技术Hash,当地址栏的Hash状态发生变化时,切换了router-view渲染的组件,来'欺骗'用户达到切换新网页的效果,hash值是不会发送给后端的,使用不需要后端配合
抽象模式:
SPA singe page app 单页应用
动态注册路由
//添加路由,按照顺序给路由末尾添加一个新路由,如果之前的路由配置里有*路由则添加的路由都不会找到,
router.addRoute({
path: "/test",
component: () => import('../test/index.vue')
})
//给name名为home的路由添加一个子路由,需要在home中添加一个router-view标签
router.addRoute('home', {
path: "/test",
component: () => import('../test/index.vue')
})
路由守卫
全局守卫router.beforeEach(function(){}):导航触发-一般用于登录验证
无论进入那个路由只要匹配一次路由,都要先调用这个函数——鉴权一般都写在这里
router.beforeEach(function(to,from,next){
//to:路由信息对象,目标路由
//from:路由信息对象,原路由(从哪里跳的),如果是直接加载网址from为空
//next():去匹配路由然后加载组件
if(to.path == '/car'){
next('/login') //去登录页面
}else{
next()
}
})
全局守卫router.beforeResolve(function(){}):
路由匹配完毕但还没有渲染会调用这个函数
router.beforeResolve(function(to,from,next){
next() //渲染组件
})
全局守卫router.afterEach(function(){})
组件已经渲染完毕后调用这个函数,一般用于操作window,例如切换组件把滚动条滚到顶部
router.afterEach(function(){
window.scrollTo(0,0)
})
路由守卫beforeEnter(function(){}):只对单个路由设置
写在路由对象内部
beforeEnter(function(){
next()
})
组件生命周期守卫beforeRouteLeave(function(){})
组件要离开时,记录用户行为和一些计时器关闭等等操作
beforeRouteLeave(function(){
next()
})
组件生命周期守卫beforeRouteEnter(function(){})
路由跳过来了,组件没有还没有激活(渲染),this用不了
beforeRouteEnter(function(){
next()
})
组件生命周期守卫:beforeRouteUpdate(function(){})
组件重用时被调用----路由传参获取参数,避免增添watch开销
导航守卫执行顺序:beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < beforeRouteEnter < beforeResolve < afterEach
vuex仓库
特点:
能够在vuex中集中管理共享的数据,易于开发和后期维护
能够高效地实现组件之间的数据共享, 提高开发效率
存储在 vuex中的数据都是响应式的,能够实时保持数据与页面的同步
一般情况下,只有组件之间共享的数据,才有必要存储到vuex中;
对于组件中的私有数据,依旧存储在组件自身的data中即可
安装
npm i vuex --save
导入:
import Vuex from 'vuex'
Vue.use(Vuex)
vuex的使用
//main.js需要引入
import store from './store'
//store/index.js
Vue.use(Vuex);
const sotre = new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
State
创建state状态,状态就是那个存数据的对象,类似于vue中的data用于存放数据
const store=new Vuex.store({
state:{msg:"仓库所有共享的数据"}
})
组件中访问数据-通过this.$store.state.xxx取出数据
this.$store.state.msg
//修改数据
this.$store.state.msg = '修改了数据' //官网不建议这样直接修改
Getter ——通过方法获取state的数据
const store=new Vuex.store({
state: {
name:"Tom"
},
getters: {
total(state){
return state.name
}
})
//使用:
this.$store.getters.total //得到state中的name数据
mutation
组件中希望更改 Vuex 的 store 中的状态(数据)的唯一方法是提交 mutation
不要用赋值表达式直接在组件中给store设置新数据
这样设计的原因是,只有通过mutation来更新数据,它才会去帮我们通知所有使用数据的组件更新数据 刷新UI
const store = new Vuex.Store({
state: {
msg:66666,
name:"Tom",
obj:{
name:"jack",
age:18
}
},
getters: {
total(state){
return state.name
}
},
mutations: {
changeAge(state,){//第一个参数传入state
state.obj.age++;
}
}
})
//组件中使用:
this.$store.commit('changeAge',{n:100})//传入了一个对象
// 以一个对象的形式传入,传入对象时,相当于把整个对象作为了第二个参数传入mutations
this.$store.commit({
type: 'changeAge', //调用的方法
n:100 //传入了一个数据
})
mutations中修改仓库数据,只能在同步代码中实现,不要在异步函数中实现,否则会出现不刷新页面的情况
Action
如果我们想要异步的修改数据,就可以使用actions异步的提交mutations
可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters
1.直接分发
//store/index.js
actions: {
changeAge(context){ //默认第一个参数传一个跟store一样的对象
context.commit('changeAge')
}
}
//组件中使用:
this.$store.dispatch('changeAge',{n:100})
2.以对象形式分发
actions: {
changeAge(context){ //默认第一个参数传一个跟store一样的对象
context.commit('changeAge')
}
}
//组件中使用:
this.$store.dispatch({
type:'changeAge',
n:100
})
Module
可用于业务分块开发:
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter
//分块设计:单独创建一个js文件作为一个模块
const moduleA = {
namespaced: true,//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)
state: { msg:1 },
mutations: { change(state,n){state.msg=n} },
actions: { change(context,n){context.commit("change",n)} },
getters: { x(state){return state.msg} }
}
const moduleB = {
namespaced: true,//局部命名空间
state: { msg:1 },
mutations: { change(state,n){state.msg=n} },
actions: { change(context,n){context.commit("change",n)} },
getters: { x(state){return state.msg} }
}
//在Vuex中添加modules属性
const store = new Vuex.Store({
modules: {
a: moduleA, //a代表使用时给模块的名字,moduleA是引入的模块
b: moduleB
}
})
//组件中的使用:
this.$store.state.a.msg // -> moduleA 的状态(数据)
this.$store.state.b.msg // -> moduleB 的状态(数据)
this.$store.commit("a/change",100)-> moduleA 的mutations
this.$store.commit("b/change",200)-> moduleB 的mutations
this.$store.getters["a/x"]-> moduleA 的getters
this.$store.getters["b/x"]-> moduleB 的getters
this.$store.dispatch("a/change",100)-> moduleA 的actions
this.$store.dispatch("b/change",200)-> moduleB 的actions
Element-ui
安装:npm i element-ui -S
完整引入:
//main.js中
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
按需引入:
//main.js中
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
//具体引入哪一个组件看官方api
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
* Vue.use(Button)
* Vue.use(Select)
*/
new Vue({
el: '#app',
render: h => h(App)
});