node -v(version)
使用npm(安装vue npm install vue)npm install -g @vue/cli
验证 vue -V 使用vue1、创建一个文件夹(文件名不能包含大写字母),进入文件夹路径下的cmd窗口
2、使用命令创建vue项目:vue create 项目名
3、选择版本信息:有vue2.0/vue3.0/自定义,三个选项,在项目真实开发中一般会用自定义,选择我们需要的插件
4、选完之后要再次进行确认选择:
5、选择vue版本,目前选择vue 2.x
6、选择哪个配置文件来配置babel等:
7、是否保存你的自定义选项,以便下次创建时直接使用:
8、等待文件创建完成:
第一步:先删除nodejs->控制面板---》程序(卸载)-----》nodejs(右键单击,卸载)
第二步:重新安装:
第三步:重启电脑
第四步:执行命令
1. node -v
2. npm cache clean --force
3. npm install
4. npm install -g @vue/cli
5. vue --version
npm run serve
npm run build
xxx.vue
代码:shift+alt+f
文件夹整理:
index.html、favicon.ico
main.js、App.vue、api、assets、lib、store、router、commponent、pages(views)
|- node_modules
:项目的依赖库
|- package.json
:依赖管理、脚本命令记录项目需要的依赖(开发&生产),部分webpack配置项、可执行脚本…
scripts(脚本命令)、devDependencies(开发依赖)、dependencies(生产依赖)、browserslist(浏览器列表)
|- babel.config.js
: webpack打包的时候,会读取这里的配置项,然后基于babel-loager把ES6代码编译为ES5
|-src
:(source)所有需要webpack编译的内容都要放在这个目录
main.js
:程序入口(webpack打包入口)App.vue
:页面入口api
:(Applicaion Programming Interface)所有接口请求管理&axios的二次封装assets
:静态资源项目需要的静态文件lib
:(library)依赖工具库(部分公司)存放抽离的JS方法store
:仓库存放vuex的管理router
:路由存放路由的管理commponent
:公有组件存放公有的组件pages(views)
:页面组件存放页面组件|-public
:存放页面模板
index.html
:默认的页面入口favicon.ico
:标题图标|-vue.config.js
:在项目根目录中,基于这些文件编写一些配置,母的是修改默认的webpack配置项
特殊:
v-pre
指令让其跳过编译)ES6Moule
或CommonJS
模块规范,分析出模块间的依赖,按照依赖打包在一起,所以需要我们的模块支持ES6Module和CommonJS规范,如果某个模块不支持这些规范:
module.exports=xxx
script src
导入,后期基于window.xxx
使用@vue/cli 为了美化项目,把写好的webpack配置项都放在 node_modules中了
配置地址:https://cli.vuejs.org/zh/config/#vue-config-js
/* 修改脚手架默认设置好的webpack配置项 */
module.exports = {
// 打包后,在index.html导入的资源,前面是 “./”「默认是 “/”」
// 例如:
// 好处:后期在服务器端进行部署的时候,不论部署到根目录还是其它目录,都可以正常访问资源
publicPath: './',
// ESLint词法检测 true/warning & false & default/error
// 下面写法:成产环境下不开启词法检测,开发环境下开启
lintOnSave: process.env.NODE_ENV !== 'production',
// 生产环境中,不编译SourceMap文件,提高打包编译的速速「SourceMap是有助于压缩后的文件调试」
productionSourceMap: false,
/*
// configureWebpack:发现默认的webpack配置项不够实现我们的需求,需要自己再次新增一些配置规则
configureWebpack: {
plugins: []
},
// chainWebpack:发现默认的webpack配置项的规则需要修改
chainWebpack: config => {
// config:默认设置好的配置项
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
return options;
});
},
*/
/*
// 设置跨域代理
devServer: {
// proxy: 'https://www.jianshu.com',
proxy: {
'/jianshu': {
target: 'https://www.jianshu.com',
changeOrigin: true
},
'/zhihu': {
target: 'https://www.zhihu.com',
changeOrigin: true
}
}
},
*/
};
publicPath:
【公共路径】打包后,指定导入文件资源的起始路径
outputDir:
【输出路径】打包成功后文件输出的路径
assets
:【静态资源】指定生成的静态页面(css/js/img/fonts)放到哪个文件夹下
pages
:【多页面】配置多页面[不同的页面,分别指定入口,出口,依赖的模块…,最终打包出多个页面]
lintOnSave
:【在保存时检测】设置ESLint语法检测的规则[“true”、“false”、“warning”、“default”、“error”]
ESlint:检测代码编写规范的,不同公司有不同规则,代码不符合规则是不允许编译的
默认值"default":【同error】开发或生产环境都需要检测,而且只要有一个不符合语法规范,则编译失败
设置为"true":【同warning】有语法规范问题,只是警告,但是编译是让其成功的
设置为"false":取消ESlint词法规范检测
工作中:开发环境下设置为true,生产环境下设为false(加快打包速度)
//获取scripts脚本命令中的ENV变量
const ENV=process.env.NODE_ENV;
//根据ENV判断
lintOnSave: ENV!=="production";
transpileDependencies
:[]【依赖关系】默认情况下,所有的node_modules中的模块,都不会基于babel-loader进行编译(也就是不会把这里的ES6转为ES5),所以为了考虑浏览器兼容,我们需要把node_modules中基于ES6代码写的模块,也进行编译!!(只不过我们使用的模块,一般都是已经编译为ES5代码的了!!)
productionSourceMap
:【生产源映射】true 在生产环境打包的时候,是否生成资源文件对应的map文件[作用:方便在线调试]
crossOrign
:【跨域访问修改请求源】当link、script标签请求服务器端资源时,将请求源改为想要的网址:http:www.baidu.com
---------------------------------高级玩家玩的------------------------------
configureWebpack
:【配置webpack】脚手架中没有设置的,我们基于configureWebpack加入进去
chainWebpack
:【链式修改webpack】脚手架设计好的,我们自己去修改[链式写法修改]
---------------------------------------end-------------------------------------
devServer
【开发服务器】:修改webpack-dev-server的配置项
devServer.proxy
:【开发服务器的代理】实现proxy跨域代理
devServer:{
proxy:{
//请求地址是以/api开始的
'/api':{
//代理到的真实服务器
target:'http://www.jianshu.com',
//把发送请求时的origin改为代理的服务器源
changeOrigin:true,
pathRewrite:{
//请求地址:'/api/user/list'
//代理服务器地址:'http://127.0.0.1:9999/api/user/list'
//真实请求服务器:'http://127.0.0.1:9999/user/list'
//把地址中的'/api'替换为""
'^/api':''
}
},
//让所有的请求都代理到这个服务器,向这个服务器发请求
'':{
target:'http://www.jianshu.com',
changeOrigin:true
}
}
}
请求地址是以/api开始的:回去找proxy中的’/api’代理
语法按照:http-proxy-middleware
真实项目中:如果请求的资源都在一个服务器上,只要设置一个’'代理即可,如果不在同一个服务器,要设置多个代理
npm i less@3 less-loader@7 --save-dev
$ npm i css-loader style-loader less less-loader autoprefixer postcss-loader –save-dev
$ less& less-loader
:实现把less编译为css$ postcss-loader & autoprefixer
:给一些不兼容的css3样式属性,自动设置前缀css-loader
:编译css代码中的@import和url这样的代码style-loader
:把编译好的css插入到页面的Head的style中,搞成内嵌式module.exports = {
//=>配置模块加载器LOADER
module: {
//=>模块规则:使用加载器(默认从右向左执行,从下向上)
rules: [{
test: /\.(css|less)$/, //=>基于正则匹配哪些模块需要处理
use: [
"style-loader", //=>把CSS插入到HEAD中
"css-loader", //=>编译解析@import/URL()这种语法
"postcss-loader", //=>设置前缀
{
loader: "less-loader",
options: {
//=>加载器额外的配置
}
}
]
}]
}
}
postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')//作用:自动添加-webkit-等前缀
]
};
package.json
// https://github.com/browserslist/browserslist
"browserslist": [//筛选浏览器的版本范围
"> 1%",
"last 2 versions"
]
main.js
import "./assets/index.less";
抽离CSS内容
style
标签中,这个插件会将css样式抽离出来形成新的css文件,并以link的方式导入到对应的html文件中==插件网址:==https://www.npmjs.com/package/mini-css-extract-plugin
下载插件:$ npm i mini-css-extract-plugin –save-dev
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
module.exports = {
plugins: [
//=>使用插件
new MiniCssExtractPlugin({
//=>设置编译后的文件名字
filename: 'main.[hash].css'
})
],
module: {
rules: [{
test: /\.(css|less)$/,
use: [
// "style-loader",
//=>使用插件中的LOADER代替STYLE方式
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader"
]
}]
}
}
MiniCssExtractPlugin.loader
代替"style-loader"
什么是babel:Babel 是一个工具链,主要用于在当前和旧浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 版本。
**babel网址:**https://babeljs.io/
需要加载的插件或加载器:
$ npm i babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime –save-dev
$ npm i @babel/runtime @babel/polyfill
各插件的作用:
$npm i babel-loader @babel/core @babel/preset-env --save-dev
$npm i @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime --save-dev
$ npm i @babel/plugin-proposal-decorators –save-dev
$ npm i @babel/runtime @babel/polyfill
module.exports = {
module: {
rules: [{
test: /\.js$/,
use: [{
loader: 'babel-loader',
options: {
//=>转换的语法预设(ES6->ES5)
presets: [
"@babel/preset-env"
],
//=>基于插件处理ES6/ES7中CLASS的特殊语法
plugins: [
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
["@babel/plugin-proposal-class-properties", {
"loose": true
}],
"@babel/plugin-transform-runtime"
]
}
}],
//=>设置编译时忽略的文件和指定编译目录
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}]
}
}
解决:需要在 index.js 引入
import '@babel/polyfill';
模块化开发的目的?
什么是组件化开发?
组件化:
以后开发项目,拿到设计稿的第一件事情,就是划分组件:
vue的pc端组件库?
element-ui
:饿了么
antdv
:蚂蚁金服iview
:京东如何在项目中使用功能性组件?
element-ui
:$npm i element-ui -s
$ npm install babel-plugin-component
vue中如何创建局部(私有)组件?
创建一个xxx.vue
文件,就是创建了一个组件,组件包含:结构、样式、功能
结构:基于template标签构建:
vue-template-compiler
插件把template语法编译为虚拟DOM[vnode],其次把本次编译出来的vnode和上次的进行比较对比,计算出差异化的部分[DOM-DIFF]最后把差异化的部分变为真实DOM放在页面中渲染样式:基于style标签构建:可以添加一些标签属性
lang="less"
:指定使用的css预编译语言[需要提前安装对应的loader]scoped
:指定当前编写的样式是私有的,支队当前组件中的结构生效,后期组件合并在一起,保证样式之间不冲突【保证样式私有化】功能:通过script标签来处理
export default{
name:'test',
//vue中的data
data(){
return {
}
}
}
导出的这个对象是VueCommponent类的实例(也是Vue的实例):对象-》VueCommponent->prototype->Vue.prototype
在对象中基于各种options api[例如:data、methods、component、watch、filters、生命周期函数…]实现当前组件的功能
如何使用一个私有组件?
需要使用私有组件的时候,需要先导入import Test from "./Test.vue";
然后注册:这样就可以调用组件进行渲染了
//3.使用组件:可以使用单闭合或双闭合
vue中如何创建全局组件?
@表示src根目录
第一步:创建一个局部组件,每一个vue文件都是一个局部组件
export default{
name:'test',
//vue中的data
data(){
return {
}
}
}
第二步:在main.js入口中,导入局部组建Vote,把其他注册为全局组件
// 创建一个全局组件
import Vote from '@/Vote.vue';
//调用方法绑定全局组件
Vue.component("Vote",Vote)
第三步:在vue的template中直接调用组件,不用再在components中注册
//3.使用组件:可以使用单闭合或双闭合
//可以在kebab-case与CamelCase之间转换
调用组件时的注意事项?
中
kebab-case
或CamelCase
,两个之间可以相互转换:
**slot插槽:**程序员开发的组件为了保证复用性和可扩展性,会在自己的组件上用
标签添加很多插槽,其他人用这个组件,可以向对应插槽插入自己想实现的代码,实现组件的扩展和复用
插槽分为了默认插槽、具名插槽、作用域插槽,我们以Test组件为例
默认插槽:只需要在调用组件
内插入我们想要的插入的html代码,会默认放到组件源代码的
插槽中
具名插槽:组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字
==作用域插槽:==把组件内部定义的数据,拿到调用组件时候的视图中使用
组件中data内的数据只能在本模块中使用,如果想让调用组件的插槽也能获取数据,就需要对组件内对的slot做bind绑定数据,调用组件的template标签做#top="AAA"
,获取数据
组件内部:
调用组件:
定义一个叫做AAA的变量,来接收插槽中绑定的所有数据(对象格式)
如果插槽名是default则使用v-slot="AAA"
或:default="AAA"
获取数据
//组件传过来的是一个对象,我们获取的AAA数据是下面的
AAA={
list:[...],
msg:...
}
1.调用组件的时候,如何操作?
调用组件的时候,我们可以基于"属性 props"把信息传递给组件
调用组件:
VueComponent
的实例组件内部:基于props注册使用进来的属性(假如:传递的属性有多个,需要使用哪些,就注册哪些即可,不注册的不能在当前组件视图中使用)
export default{
//1、基于props注册的属性,会直接挂载到当前组件的实例上(可以直接在视图上基于title进行渲染)->this.title或者this.$props.title
//2、属性title做了get/set劫持
//保证传递进来的属性值发生改变,当前组件可以重新渲染
//3、传递进来的属性是"只读"的;我们可以拿来使用,但是不允许在组件内部去修改,想要修改只能重新调用组件传递新的信息进来=>this.title=xxx(错误的)
props:["title",...]
}
props可以是对象的格式,这样在注册属性的同时,也可以做属性的规则校验
props:{
//title:String,设定传递属性类型
//title:[String,Number],可以是多个类型
title:{
type:String,
required:true//设置为必传:如果没传这个属性,控制台抛出警告,视图正常渲染
},
supNum:{
type:Number,//传递的类型和要求不一致,控制台也会有警告
default:0//(不是必传)如果不传递值,使用默认值
}
}
每创建一个组件其实相当于创建一个自定义类,而调用这个组件就是创建VueCommponent(或者Vue)类的实例
什么是虚拟DOM对象?
虚拟DOM对象:_vnode
,作用:
原理:vue内部自己定义的一套对象,基于自己规定的键值对,来描述视图中每一个节点的特征:
第一步:基于vue-template-compiler
去渲染解析 template 视图,最后构建出上述的虚拟DOM对象
第二步:组件重新渲染,又重新生成一个 _vnode
第三步:对比两次的 _vnode. 获取差异的部分
第四步:把差异的部分渲染为真实的DOM
真实DOM
let elem=document.createElement(_vnode.tag);
....
#app.appendChlild(elem);
实际开发场景?
组件中的script中存在的状态值和属性值?
_vode
对象的私有属性中(所以状态值和属性值名字不能重复)template标签
中调用状态值和属性值,不需要加this,直接调用状态名或属性名script标签
中调用状态值和属性值,需要加this调用vue的pc端组件库?
element-ui
:饿了么
antdv
:蚂蚁金服iview
:京东如何在项目中使用功能性组件?
element-ui
:$npm i element-ui -s
$ npm install babel-plugin-component
在Vue中我们基于scoped设置样式私有化之后:
会给组件创建一个唯一的ID(例如:data-v-5f109989
)
在组件视图中,我们编写所有元素(包含元素调用的UI组件),都设置了这个ID属性;但是我们调用的组件内部的元素,并没有设置这个属性!!
总结:只要是自己写的(含插槽内容)、以及直接调用的组件都设置这个属性(Attribute):但组件内部元素是不设置的
<div data-v-5f1969a9 class="task-box">
<button data-v-5f1969a9 type="button" class="el-button el-button--primary">
<span>新增任务</span>
</button>
</div>
而我们编写的样式,最后会自动加上属性选择器:
.task-box {
box-sizing: border-box;
...
}
编译后成为:
.task-box[data-v-5f1969a9]{
box-sizing: border-box;
}
组件样式私有化的原理:设置唯一的属性(组件ID)、组件内部给所有样式后面都加上该属性选择器
问题:组件内部的元素没有设置这个属性,但是我们编写的样式是基于这个属性选择器在css设置的选择器,
解决:在组件内部的元素选择器前加/deep/
:
*,/deep/.el-textarea__inner,
/deep/.el-input__inner{
border-radius: 0;
}
在真实项目中,我们会把数据请求和axios的二次封装,都会放到src/api路径下进行管理
小技巧:
//main.js
import api from '@/api/index';
// 把存储接口请求的api对象挂载搭配Vue的原型上:后续在各个组件基于this.$api.xxx()就可以发送请求了,无需在每个 组件中再单独导入这个api对象。
Vue.prototype.$api=api;
组件传参的分类7种:
父组件向子组件传参:props
首先你要有父组件跟子组件
第一步:父组件在组件调用标签中自定义属性
<Coma msg="hello" :numa="num">Coma>
第二步:子组件通过props接收(数组,对象)
// props的值是只读的
props:["msg","numa"]
props中的属性是只读的,子组件不能修改这些值,否则会报错
props可以是对象或数组类型,对象可以对数据做校验,数组不能
子组件向父组件传参,基于发布订阅(@xxx给子组件标签自定义事件、$emit)
第一步:父组件在调用子组件的标签上需要自定义一个事件,这个事件及绑定的方法就会添加到子组件的事件池中:底层实质上是调用了this.$on(“myEvent”,fn)
<Coma @myEvent="getData"></Coma>
第二步:在子组件中执行父组件给自己定义的事件,用this.$emit("myEvent",parm1,parm2,...)
方法
methods:{
transferData(){
//第一个参数是要执行的事件
//第二个参数及以后的参数是传的数据值
this.$emit("myEvent",this.msg,this.flag)
}
}
第三步:定义getData的方法
methods:{
//n就是子组件传的msg
//flag就是子组件传的flag
getData(n,flag){
this.n=n;
this.flag=flag
}
}
发布订阅( o n 、 on、 on、emit)
b—>c传数据
第一步:全局的main.js中
// 创建一个全局的EventBus
let EventBus=new Vue();
//将EventBus放在Vue的原型上,通过$bus来调用
Vue.prototype.$bus=EventBus;
$bus.$on()
绑定的事件函数,在哪个vue实例上都可以基于$bus.$empty()
执行,还可以传值第二步:comc向事件池中绑定事件:this.$bus.$on("事件名",函数)
created(){
this.$bus.$on("myEvent",(m)=>{
this.msg=m;
})
}
第三步:comb从事件池中获取事件函数并执行:this.$bus.$emit("事件名",想传的参数)
methods:{
transferData(){
this.$bus.$emit("myEvent",this.msg)
}
}
(provide[提供],inject[接收])
( p a r e n t 、 parent、 parent、children[n]、 r o o t 、 root、 root、refs)
)都会产生一个私有的vue实例,获取之后就可以基于这个元素获取其中的数据或方法了:
this.$parent
:相对于子vue实例来说,获取父元素的整个vm实例,每个vue实例都只存在一个父实例
this.$children[n]
:获取第n个子元素的vm实例
created
生命周期函数里或之后获取子元素【父子组件的生命周期】this.$root
:获取根元素的vm实例(main.js中new 的Vue实例)this.$refs
:this的子元素中需要定义ref
属性:比如ref="xxx"
:
this.$refs.xxx
获取的是DOM对象this.$refs.xxx
获取的是子组件的vm实例父组件绑定在子组件标签中的事件,是无法触发的,如何解决?
@xxx.native
: 监听组件根元素的原生事件。
例子:
原理:在父组件中给子组件绑定一个==原生(click/mouseover…)==的事件,就将子组件变成了普通的HTML标签,不加’. native’父组件绑定给子组件标签的事件是无法触发的
一、父子组件生命周期执行过程:
beforeCreated
created
beforeMount
beforeCreate
created
beforeMount
mounted
mounted
二、子组件更新过程:
berforeUpdate
berforeUpdate
updated
updated
三、父组件更新过程:
berforeUpdate
updated
四、父组件销毁过程:
beforeDestory
beforeCreate
created
destoryed
五、父组件销毁子组件时执行的过程:
beforeUpdate
beforeDestory
destoryed
updated
vuex:状态管理库**(公共大仓库)**,集中式存储管理所有组件的状态值,相当于把所有Vue组件的状态数据放到一个公共数据库中,任何一个组件修改了公共数据库中的状态值,其他使用了该状态值的组件都会跟着刷新渲染这个值
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源“而存在。这也意味着,每个应用将仅仅包含一个 store 实例
vuex的作用:
localStorage
:本地存储,刷新有数据、数据永久sessionStorage
:会话存储,刷新有数据、关闭页面没有数据vuex
:不刷新,不关闭页面,就会保存以前的数据,刷新就不存在数据vuex:“单向数据流”理念:
vuex的异步函数执行的过程:
state
:相当于vue中的data
getters
:相当于vue中的computed
mutations
:相当于vue中的methods(同步)
actions
:相当于vue中的methods(异步)
Modules
:模块
在讲解vuex的五个核心属性之前需要先创建一个vuex仓库的实例:
//新建index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)//在vue上挂载vuex包,扩展
const store = new Vuex.Store({//创建Vuex的store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store;
//在main.js中挂载,注意,这个store实例是挂载到了Vue的prototype上
new Vue({
el: '#app',
store: store,
})
state
:相当于vue中的data
作用:存放所有组件能够共用的状态值
用法:
vuex实例仓库端定义:
const store = new Vuex.Store({
//在大仓库中存放一个count为0
state: {
count: 0
}
})
所有vue组件端调用:
//方式一:直接沿着原型链调用(不推荐没缓存)
{{this.$store.state.count}}
//方式二:放到computed中调用(有缓存)
computed: {
count () {
return this.$store.state.count
}
}
//方式三:使用辅助方法,放到computed中调用多个(推荐,有缓存)
import { mapState } from 'vuex'//导入辅助函数Vuex.mapState,它返回的是一个对象
//第一种:计算属性中只存在映射过来的,参数是数组或对象,以数组为例:
computed: mapState(['count'])// 映射 this.count 为 store.state.count
//第二种:计算属性中存在映射过来的也存在自定义的,参数是数组或对象,以对象为例:
computed: {
addCount(){},
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
//...
})
}
getters
:相当于vue中的computed
作用:在vue组件获取state状态值之前,对状态值做格式化,再传给vue组件
参数:
state
:【默认参数】指向的是vuex的实例store里的stategetters
:【默认参数】指向的是vuex的实例store里的getters,可以根据它,获得getters中其他属性的信息用法:
vuex实例仓库端定义:
getters: {
getCountAdd: (state,getter )=> {
return state.count+1+getter.getc;//0+1+666
}
getc(){return 666}
}
所有vue组件端调用:在computed中调用
import { mapGetters } from 'vuex';//导入辅助函数
//方式一:以数组作为参数执行辅助函数
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'getCountAdd',
'getc',
// ...
])
}
//方式一:以数组作为参数执行辅助函数,可以自定义状态名
computed: {
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
}
mutations
:相当于vue中的methods(同步)
要点:mutations中的函数必须是同步的,不能放异步方法
作用:vue组件不能直接修改store大仓库中的状态值,(会导致页面与仓库不同步),如果想做到所有组件数据,与store内state状态值同步,就要经过mutations中的方法来修改
参数:
state
:【默认参数】指向当前vuex实例store的state对象payload
:载荷,【第二个参数】用来接收组件传给函数的参数,如果想传多个数据,载荷应该是一个对象用法:
vuex实例仓库端定义:
mutations: {
//自增函数
increment (state, payload) {
state.count += payload.amount
}
}
所有vue组件端调用:在methods中调用,
//方式一:使用store.commit方法可直接调用
methods:{
addCount(){
this.$store.commit('increment', {
amount: 10
})
}
}
//方式二:使用辅助函数,参数为对象或数组
import {mapMutations} form "vuex";//导入vuex的辅助函数
methods:{
...mapMutations(["increment"]),//数组传参
...mapMutations({//对象传参
inc:"increment"
})
}
//调用
<telement>
<div>
<button @click="inc(1,2,3)">点击 </button>
</div>
</telement>
actions
:相当于vue中的methods(异步)
作用:mutation只能存储同步函数,actions就是将同步函数封装为异步函数
参数:
context
:【默认参数】指向vuex的实例store大仓库用法:
vuex实例仓库端定义:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
//定义自增方法
increment (context) {
//异步函数
setTimeOut(function(){
//内部再调用了commit方法
context.commit('increment')
},1000)
}
}
})
所有vue组件端调用:在methods中调用
//方式一:使用store的dispatch方法调用触发
methods:{
addCount(){
this.$store.dispatch('increment')
}
}
//方式二:使用辅助函数,参数为对象或数组
import {mapActions} form "vuex";//导入vuex的辅助函数
methods:{
...mapActions(["increment"]),//数组传参
...mapmapActions({//对象传参
inc:"increment"
})
}
//调用
<telement>
<div>
<button @click="inc(1,2,3)">点击 </button>
</div>
</telement>
作用:减少单一状态树的shore对象存放数据导致的臃肿,将整个sotre对象拆分为多个模块module,每个模块都存在自己局部的state、getters、mutations、actions、namespaced
,甚至仍然将这个模块继续向下拆分
namespaced:默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
我们在...mapGetters("moduleA",["msg"])
,寻找的是moduleA
模块中的msg状态值,如果没有写moduleA
的话,它会直接去全局的store中找状态值msg
用法:
第一步:需要创建一个独立的模块:
//store/moduleA.js
//就是vuex实例类型的对象
const mouduleA={
//存在state,mutations,actions,getters,namespaced。。。
state:{
msg:"我是moduleA",
arr:[1,2,3,4,5,6]
},
getters:{
getNum(state){
return state.arr.filter(item=> item%2==0)
}
}
}
//导出模块
export default moduleA;
第二步:在store实例中导入并挂载:
modules: {
a:moduleA
}
})
//重点:
//store挂载moduleA之后:
//moduleA中的state对象会挂载到store的state对象上
store:{
state:{
a:{//重点:a指向的就是moduleA模块中的state对象
msg:"我是moduleA";
}
}
//moduleA中的mutations、getters、actions对象会挂载到store的对应对象上
//以getters为例
store:{
getters:{
"a/getNum":[2,3,4];//将子模块中计算属性的计算结果放入getters中
}
//mutations、actions相同做法
}
}
第三步:在vue组件中调用vuex的modules中:
//方式一:通过$store导入
this.$store.state.a.msg;//获取a模块中的属性
this.$store.getters["a/getNum"];//获取方法或计算属性
//方式二:通过辅助函数写法(常用)
import {mapState,mapGetters} from "vuex";
computed:{
...mapState("a",["msg"]),//a是nameSpace的命名空间,所以必须开启namespace才行
...mapState(["a/msg"]),//这样获取不了,因为state的挂载方式不是这样挂载的
...mapGetters(["a/getNum"]),//这样能获取,以为getters、mutations、actions的挂载方式
}
//方式三:通过导入方法指定mapxxx的地址
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('a')
computed:{
...mapState(["msg"]),//它会直接找a里的msg,因为上面设置了命名空间
}
mapState()获取的是大仓库state属性中的状态值
mapGetters()获取的是大仓库getters属性中的状态值
mapMutations()获取的是大仓库mutations属性中的方法
mapActions)获取的是大仓库actions属性中的方法
在真实项目中我们不会使用{{this.$store.state.count}}
来调用仓库中的状态值,以为这样每次页面渲染或打开页面都需要去大仓库中获取(无缓存)
所以我们基于computed(有缓存)的优势,来获取仓库中的数据,这时就需要调用到vuex中内置的辅助函数:mapState()
;
第一步:导入mapState
辅助函数
//Home.js
import mapState from "vuex";
第二步:在计算属性computed
中配置mapState,mapState返回的是一个对象
mapState(param):辅助函数的参数可以是对象或数组
方式一:computed中只需要大仓库中的mapState
//1、mapState函数中的参数是数组,会默认返回对应的num和age状态值,我们调用时也直接调用num age即可
computed:mapState(["num","age"])
//2、mapState函数中的参数是对象,会默认返回对应num和age状态值,并且赋值给调用方的私有属性n和a,我们调用时用n和a调用
computed:mapState({
n:"num",
a:"age"
})
方式二:computed中既有自己定义的计算属性,又用辅助函数获取的
computed:{
getData(){
return aaa;
},
//因为mapState中获取的是对象,所以需要通过展开运算符将其展开
...mapState(
["num","age"]
)
}
注意:mapState获取的是对象,需要展开运算符展开
mapGetters()获取的是大仓库getters属性中的状态值(必须是同步的方法)
vuex实例的getters属性相当于vue实例中的computed属性
第一步:在vuex实例中添加getters属性,并定义格式化方法:
//store/index.js
getters:{
//第一个参数是当前vuex实例的state属性对象
//第二个参数是当前vuex实例的getters属性对象
addCount(state,getters){
return state.count++;
}
}
第二步:在调用方实例中导入,并引入
//Home.vue
import mapGetter from "vuex";//导入mapGetter
computed: {
//方式一:直接在computed中调用:通过Vue公有属性中挂载的$store调用
addCount () {
return this.$store.getters.addCount
}
//方式二:通过mapGetter调用
...mapGetters(["addCount"]);//数组参数形式获取,不能改状态名
...mapGetters({
add:"addCount";//对象形式获取,可以改状态名
})
}
mapMutations()获取的是大仓库mutations属性中的状态值
**第一步:**在vuex中创建mutations属性
mutations: {
addCount (state) {
// 变更状态值
state.count++
}
第二步:在Home.vue中导入mapMutations并注册到实例methods上
export default {
// ...
methods: {
...mapMutations([
'addCount', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapMutations({
add: 'addCount' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
mapActions获取的是大仓库actions属性中的方法
第一步:在vuex实例中添加actions方法(必须是异步的):actions的作用就是将mutations里的同步方法异步化
//store/index.js
actions: {
//context是指向当前vuex实例的对象,也就是$store,所以能调用它上面的所有方法
increment (context) {
context.commit('addCount')
}
}
第二步:在Home.vue中导入,并注册到自己身上
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'addCount', // 将 `this.addCount()` 映射为 `this.$store.dispatch('addCount')`
// `mapActions` 也支持载荷:
'addCount' // 将 `this.addCount(amount)` 映射为 `this.$store.dispatch('addCount', amount)`
]),
...mapActions({
add: 'addCount' // 将 `this.add()` 映射为 `this.$store.dispatch('addCount')`
})
}
}
问题:没有用缓存之前,我们每次打开页面都会发送请求
目标:应用vuex实现缓存,只要页面不刷新,不关闭,数据如果是null,我就发送请求,如果不是null就从vuex的缓存中拿数据
实现:
第一步:创建异步方法获取服务器数据,并把数据赋值给状态值
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './moduleA/moduleA'
import api from "@/api/index.js";
Vue.use(Vuex)
//store实例
export default new Vuex.Store({
state: {
// 第一步:list专门存放数据
list:null
},
mutations: {
// 第二步:修改list的值,如果请求成功就不为null了
changelist(state,payload){
state.list=payload;
}
},
actions: {
//第三步:异步函数需要写在actions中
async getData(context){
let result=await api.getTaskList();
if(result.code===0){//返回数据成功
context.commit("changelist",result.list);//异步函数想修改state状态值,必须经过mutations
}
}
},
modules: {
a:moduleA
}
})
第二步:在页面加载之前,先判断是否有缓存,如果有直接用
//Three.vue
{{this.$store.state.list}}
第一步:安装vue和vuex
模块包:$npm i vue vuex -S
第二步:导入vue和vuex
//为了防止导入的包太大,所以按需导入
// /store/index.js
import vue from "vue";
import Vuex from "vuex";
第三步:Vue注册vuex并导出
// /store/index.js
import vue from "vue";
import Vuex from "vuex";
//Vue注册Vuex
Vue.use(Vuex)
//创建vuex的实例
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
//导出创建好的vuex实例
export default store;
第四步:在main.js中导入、挂载已经创建好的实例
//main.js
import store from "@/store/index.js";
new Vue({
router,
//store被挂载到了全局Vue实例的prototype上,任何实例都可以利用this.$store获取到store大仓库实例
store,
render: h => h(App)
}).$mount('#app')
第五步:任意组件可以调用修改仓库中的信息,但是想修改数据,都必须通知仓库(调用仓库mutations)中的方法,来实现页面数据与仓库数据的同步修改
面试题:能不能在界面上直接修改仓库中获取的数据:能!,但是修改的数据只是在页面上显示修改了,因为没有经过仓库,仓库中的数据实质上并没有修改,所以我们在工作中不能直接修改仓库中获取的值。
//Home.vue
<div class="home">
{{this.$store.state.count}}//调用大仓库中的count
<button @click="changeData">addbutton>//修改仓库中的count
div>
<script>
methods:{
//Home.vue
changeData(){
//使用store.commit来执行store中mutations中的方法
store.commit('increment')
}
}
script>
// /store/index.js
const store = new Vuex.Store({
state: {
//放到大仓库中的状态值,所有组件都可以用这个count
count: 1000
},
//存放方法(同步)
mutations: {
//状态值自增的方法
increment (state) {
state.count++
}
}
})