必备-10.@vue/cli

必备-10.@vue/cli

@vue/cli是什么

  • @vue/cli(Command Line Interface):vue的命令行接口,我们称之为脚手架,其作用是帮助我们快速生成一个可以放到服务器上的项目
  • 脚手架:把项目中需要的webpack配置项目都内部处理好,基于脚手架快速创建一个工程化
  • 使用网址:https://cli.vuejs.org/zh/guide/installation.html

@vue/cli创建一个项目

第一步:安装@vue/cli

  • 使用vue必须有nodejs
    • 安装nodejs:(https://nodejs.org/en/) 验证 node -v(version) 使用npm(安装vue npm install vue)
    • 安装vue-cli:npm install -g @vue/cli 验证 vue -V 使用vue

第二步:创建项目文件

  • 1、创建一个文件夹(文件名不能包含大写字母),进入文件夹路径下的cmd窗口

  • 2、使用命令创建vue项目vue create 项目名

  • 3、选择版本信息:有vue2.0/vue3.0/自定义,三个选项,在项目真实开发中一般会用自定义,选择我们需要的插件

    • image-20211205171428684
      • Babel :兼容es6
      • TypeScript : javascript的超集
      • image-20211205171519298
  • 4、选完之后要再次进行确认选择

    • image-20211205171636844
    • node-sass是自动编译实时的,dart-sass需要保存后才会生效
  • 5、选择vue版本,目前选择vue 2.x

    • image-20211205171733341
  • 6、选择哪个配置文件来配置babel等:

    • image-20211205171857892
      • In dedicated config files:在专用配置文件中
      • In package.json:在package中
  • 7、是否保存你的自定义选项,以便下次创建时直接使用:

    • image-20211205172242517
  • 8、等待文件创建完成:

    • image-20211205172533415
项目创建失败怎么办
  • 第一步:先删除nodejs->控制面板---》程序(卸载)-----》nodejs(右键单击,卸载)

  • 第二步:重新安装:

    • 重新安装 node:https://www.cnblogs.com/liuqiyun/p/8133904.html
  • 第三步:重启电脑

  • 第四步:执行命令

    • 1. 	node -v
      2.	npm cache clean --force 
      3. 	npm install
      4. 	npm install -g @vue/cli
      5.	vue --version
      

第三步:启动项目

  • cd my-project
  • npm run serve
    • 开发环境启动项目npm run serve
    • 生产环境启动项目npm run build

第四步:访问项目

  • image-20211205203228293
    • local:本地访问的路径
    • **network:**网络访问的路径

第五步:vscode使用Vue

  • 安装Vetur插件->重启vscode->格式化xxx.vue代码:shift+alt+f

vue项目的基本结构

image-20211205205041623


  • 文件夹整理:

    • node_modules
    • package.json
    • babel.config.js
    • vue.config.js
    • public:index.html、favicon.ico
    • src: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配置项

特殊:

  • 一般来讲,public/index.html页面模板中不会编写啥内容,最后都是基于webpack把各组件(或各页面)的内容合并编译后,插入到index.html的#app的容器中
  • 但是部分内容还是需要在这个页面模板中写一写的:
    • 我们后期可以基于vue3中的传送门技术,把组件中的部分内容,插入到页面除#app外的其他部分容器中,此时我们需要在页面中写一些其它容器!!
    • 我们也可以把一些纯静态的内容,而且是不想基于vue的模板编译的,写死在页面模板中!(这样做的很少,因为即使写在组件中,我们也可以基于v-pre指令让其跳过编译)
    • 因为webpack最后会把项目中的css或js合并压缩打包为一个文件[我们后期会做分割打包,例如:路由懒加载],并且插入到页面中,这样在页面渲染的时候,加载CSS或JS就会慢,如果我们想在没有加载完这些文件之前,先呈现给用户Loading(或骨架屏)效果,来减少白屏事件,那么我们就在页面模板中,单独写一写骨架屏的实现,这些代码,是不能和主要内容打包在一起的。
    • webpack打包,是按照ES6MouleCommonJS模块规范,分析出模块间的依赖,按照依赖打包在一起,所以需要我们的模块支持ES6Module和CommonJS规范,如果某个模块不支持这些规范:
      • 方式一:手动改为支持的:module.exports=xxx
      • 方式二:在页面模板中,基于script src导入,后期基于window.xxx使用

各配置文件代码解析

package.json

  • 默认的生成的package.json:不仅可以编译 es6语法,也可以编译vue语法

image-20211205205546729

babel.config.js

image-20211205205842513

public和src

image-20211205205951152

重写webpack配置

  • @vue/cli 为了美化项目,把写好的webpack配置项都放在 node_modules中了

    • 想要修改其配置项,就需要自己在package.json同级目录下创建 vue.config.js 文件,在文件中修改它的一些配置【vue脚手架设置好的规则】
  • 配置地址:https://cli.vuejs.org/zh/config/#vue-config-js

  • /* 修改脚手架默认设置好的webpack配置项 */
    module.exports = {
        // 打包后,在index.html导入的资源,前面是 “./”「默认是 “/”」
        // 例如: <script src="js/chunk-vendors.ffeebf7c.js"></script>
        // 好处:后期在服务器端进行部署的时候,不论部署到根目录还是其它目录,都可以正常访问资源
        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:输出路径】打包成功后文件输出的路径

    • 默认值"":会默认放到dist目录项
  • assets:【静态资源】指定生成的静态页面(css/js/img/fonts)放到哪个文件夹下

    • 默认值"":css文件放到css文件夹下,js文件放到js文件夹下
  • pages:【多页面】配置多页面[不同的页面,分别指定入口,出口,依赖的模块…,最终打包出多个页面]

  • lintOnSave:【在保存时检测】设置ESLint语法检测的规则[“true”、“false”、“warning”、“default”、“error”]

    • ESlint:检测代码编写规范的,不同公司有不同规则,代码不符合规则是不允许编译的

      • 到工作中,根据项目中设定的ESLint规则,需要在vs中基于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文件[作用:方便在线调试]

    • 真实开发时:设为false,以此让打包速度更快,而且我们不允许在线调试的
  • 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

    • 真实项目中:如果请求的资源都在一个服务器上,只要设置一个’'代理即可,如果不在同一个服务器,要设置多个代理

vue 使用 less

  • vue中已经配置好了less的转换配置,如果想用,直接安装less需要的包就可以
  • 安装less加载器:npm i less@3 less-loader@7 --save-dev
    • **注意:**使用低版本,否则报错,脚手架不兼容高版本

vue项目的兼容性

  • 检查版本兼容网址:https://cli.vuejs.org/zh/guide/browser-compatibility.html#browserslist
  • 注意:vue的核心是基于Object.defineProerty 实现数据劫持的,所以及不论怎么处理,IE8及以下都不兼容
    • 方案一:调整 package.json 中的 browserslist 配置项中
      • css3的样式会基于这个列表自动设置不同的前缀
      • es6等代码也会基于这个列表,自己进行语法编译
    • 方案二:基于@babel/polyfill 进行处理

兼容CSS

  • **功能:**webpack不能识别less格式文件,当我们遇到这样格式的文件时,需要通过loader进行一系列处理,让它变得能让webpack编译

$ npm i css-loader style-loader less less-loader autoprefixer postcss-loader –save-dev

  • 各个loader加载器的功能:
    • $ 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";
**mini-css-extract-plugin **

抽离CSS内容

  • 功能:默认的css样式会插入到html文件的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"

兼容ES6

  • 什么是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
      
  • 各插件的作用:

    • 处理普通的JS语法$npm i babel-loader @babel/core @babel/preset-env --save-dev
    • 处理class的语法:$npm i @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime --save-dev
    • 处理 promise 等特殊语法:$ 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/
        }]
    }
}

兼容Promise

  • 基于webpack 配置规则,这个class 编译成为ES5语法,但是Promise无法编译

image-20211205165735760

解决:需要在 index.js 引入

  • 基于@babel/polyfill处理ES6内置方法的兼容「例如:promise,polyfill中自己实现了一套兼容的promise」

import '@babel/polyfill';

image-20211205165646468

模块化

模块化开发的目的?

  • 模块化主要针对于js逻辑性文件,将JS文件的主要功能暴露出去,供其他模块复用,这就是模块化

组件化

什么是组件化开发?

  • 组件化

    • 功能性组件:【UI组件库中提供的一般都是功能性组件:element/iview/antdv/vant/cube…】
      • 一般UI组件库提供的功能组件就够用了
      • 偶尔UI组件库当中不存在的,才需要自己封装【难点
      • 我们经常会把功能性组件进行二次封装,(结合自己的业务逻辑)【特色亮点
    • 业务性组件
      • 通用业务性组件:好多页面都需要用到的,我们把其封装为公共的组件
      • 普通组件
  • 以后开发项目,拿到设计稿的第一件事情,就是划分组件:

    • 原则:按照功能板块划分、本着复用性原则、拆的越细越好(这样才能更好的实现复用)

vue的pc端组件库?

  • element-ui:饿了么
    • https://element.eleme.cn/#/zh-CN/
    • vue2.xx:elemnetui
    • vue3.xx:element plus
  • antdv:蚂蚁金服
  • iview :京东

如何在项目中使用功能性组件?

  • 第一步:安装element-ui:$npm i element-ui -s
  • 第二步:导入:
    • 完整导入:整个组件库都导入进来,想用什么直接用Vue.use(xxx)即可
      • 缺点:如果我们只用几个组件,则无用的导入组件会造成项目打包体积变大[不好],所以项目中推荐使用按需导入
    • 按需导入
      • 需要安装依赖$ npm install babel-plugin-component

image-20211207125311304

局部组件

vue中如何创建局部(私有)组件?

  • 创建一个xxx.vue文件,就是创建了一个组件,组件包含:结构、样式、功能

    • 结构:基于template标签构建:

      • 只能有一个根元素节点(vue2中)
      • vue的视图是基于template语法构建的(各种指令&小胡子…),最后vue会把其编译为真实DOM插入到页面指定的容器中:
        • 首先基于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、生命周期函数…]实现当前组件的功能

        • 在组件中data不再是一个对象,而是一个"闭包"

如何使用一个私有组件?

  • 需要使用私有组件的时候,需要先导入import Test from "./Test.vue";

  • 然后注册:这样就可以调用组件进行渲染了

    • <template>
        <div id="app">
            //3.使用组件:可以使用单闭合或双闭合
          <Test></Test>
            <Test>
        </div>
      </template>
      
      
      <script>
      //1、导入组件
      import Test from "./Test.vue";
      export default {
        name: "App",
        components:{
          //2、注册使用的组件
          Test,
        }
      };
      </script>
      

全局组件

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之间转换
        <Test></Test>
          <Test>
      

调用组件时的注意事项?

  • 调用组件的时候,可以使用:
    • 双闭合:<Test></Test>
      • 双闭合的方式可以使用插槽(slot)
      • 插槽的作用:让组件具备更高的复用性(或扩展性)
      • 我们封装好一个组件,把核心部分都实现了,但是我们期望用户调用组件的时候,可以自定义一些内容,防止在已经封装好的组件内部:
        • 第一步:组件内部留好位置<slot></slot>
        • 第二步:把双闭合标签中的内容插入到<slot></slot>
        • image-20211206181536410
    • 单闭合:</Test>
    • 组件的调用可以用kebab-caseCamelCase,两个之间可以相互转换:
      • 官方推荐调用时用kebab-case,其他时候用CamelCase

插槽

**slot插槽:**程序员开发的组件为了保证复用性可扩展性,会在自己的组件上用<slot></slot>标签添加很多插槽,其他人用这个组件,可以向对应插槽插入自己想实现的代码,实现组件的扩展和复用

  • 插槽分为了默认插槽、具名插槽、作用域插槽,我们以Test组件为例

    • 默认插槽:只需要在调用组件<Test><Test>内插入我们想要的插入的html代码,会默认放到组件源代码的<slot name="default"></slot>插槽中

    • 具名插槽:组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字

      • 在调用组件<Test><Test>内自己写的代码,我们用template包裹代码,并把v-slot:xxx写在template上,这时就会将xxx里面的代码,包裹到组件源代码的<slot name=”xxx“></slot>的标签中

      • 组件内部:<slot name="xxx"> 默认名字是default

      • ==调用组件:==需要把v-slot写在template上

        •     <template v-slot:xxx>
                 ...
              </template>
              <template>
                 ...
              </template>
          
        • v-slot可以简写为#:#xxx

        • image-20211206195902691

    • ==作用域插槽:==把组件内部定义的数据,拿到调用组件时候的视图中使用

      • 组件中data内的数据只能在本模块中使用,如果想让调用组件的插槽也能获取数据,就需要对组件内对的slot做bind绑定数据,调用组件的template标签做#top="AAA",获取数据

      • 组件内部:<slot name="top" :list="list" :msg="msg"></slot>

        • 把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg
      • 调用组件:<template #top="AAA"></template>

        • 定义一个叫做AAA的变量,来接收插槽中绑定的所有数据(对象格式)

        • 如果插槽名是default则使用v-slot="AAA":default="AAA"获取数据

             	//组件传过来的是一个对象,我们获取的AAA数据是下面的
          AAA={
                list:[...],
                msg:...
              }
          
        • image-20211206185215519

父子组件之间数据传参

1.调用组件的时候,如何操作?

  • 调用组件的时候,我们可以基于"属性 props"把信息传递给组件

    • 原因:如果组件中展示的信息不固定,需要调用的时候告诉组件,此时我们就可以基于属性处理了
      • 如果只是想传递一些数据给组件,我们基于属性处理
      • 如果想把一些自定义的结构和内容插入到组件指定的信息,那么我们就需要使用插槽
    • 两者最终目的:都是支持用户自己定义组件呈现的内容实现复用和扩展
  • 调用组件

    • <Vote title="xxx" :supNum="10" :oppNum="2"></Vote>
      • 我们传给组件的值,默认是字符串类型的,比如title
      • 如果想传数字类型的,则需要调用v-bind或冒号的形式传值
      • 我们每调用一次Vote,都会生成一个独立的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)类的实例

    • 实例(this)->VueComponent.prototype->Vue.prototype->Object.prototype
    • 当前实例可以访问Vue.prototype上的一些公共属性和方法
虚拟DOM

什么是虚拟DOM对象?

  • 虚拟DOM对象:_vnode,作用:

    • 原理:vue内部自己定义的一套对象,基于自己规定的键值对,来描述视图中每一个节点的特征:

      • tag标签名
      • text文本节点,存储文本内容
      • children:子节点
      • data:属性
    • 第一步:基于vue-template-compiler去渲染解析 template 视图,最后构建出上述的虚拟DOM对象

    • 第二步:组件重新渲染,又重新生成一个 _vnode

    • 第三步:对比两次的 _vnode. 获取差异的部分

    • 第四步:把差异的部分渲染为真实的DOM

      • 真实DOM
        let elem=document.createElement(_vnode.tag);
        ....
        #app.appendChlild(elem);
        

实际开发场景?

  • 传递给组件的属性是==“只读的==”,如果直接把属性渲染到视图中,渲染的内容就固定了,但是组件内部后续的某些操作,想修改呈现的内容,我们肯定不能修改属性值,那么该如何处理?
    • 第一步:把传递的属性接收后,赋值给组件的状态
    • 第二步:视图渲染的是状态,后期改变的也是状态即可
状态值&属性值

组件中的script中存在的状态值和属性值?

  • 状态值:data中的数据值称为状态值
  • 属性值:props中的数据值称为属性值
  • 状态值和属性值是直接挂载到_vode对象的私有属性中(所以状态值和属性值名字不能重复)
  • 我们在视图template标签中调用状态值和属性值,不需要加this,直接调用状态名或属性名
  • 我们在功能script标签中调用状态值和属性值,需要加this调用
  • computed(计算属性):也是挂载实例上的,所以他们三个都不能重名
组件库

vue的pc端组件库?

  • element-ui:饿了么
    • https://element.eleme.cn/#/zh-CN/
    • vue2.xx:elemnetui
    • vue3.xx:element plus
  • antdv:蚂蚁金服
  • iview :京东

如何在项目中使用功能性组件?

  • 第一步:安装element-ui:$npm i element-ui -s
  • 第二步:导入:
    • 完整导入:整个组件库都导入进来,想用什么直接用Vue.use(xxx)即可
      • 缺点:如果我们只用几个组件,则无用的导入组件会造成项目打包体积变大[不好],所以项目中推荐使用按需导入
    • 按需导入
      • 1、需要安装依赖$ 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;
         		 }
        

API

  • 在真实项目中,我们会把数据请求和axios的二次封装,都会放到src/api路径下进行管理

  • 小技巧

    • //main.js
      	import api from '@/api/index';
      	// 把存储接口请求的api对象挂载搭配Vue的原型上:后续在各个组件基于this.$api.xxx()就可以发送请求了,无需在每个		组件中再单独导入这个api对象。
      	Vue.prototype.$api=api;
      

组件传参

组件传参的分类7种:

  • 父组件向子组件传参:props
  • 子组件向父组件传参:发布订阅(@xxx给子组件标签自定义事件、$emit)
  • 组件相互传参(兄弟):发布订阅( o n 、 on、 onemit)【2和3传参是一种】
  • 祖先向后代传参==(provide[提供],inject[接收])==
  • vue实例属性传参==( p a r e n t 、 parent、 parentchildren[n]、 r o o t 、 root、 rootrefs)==
  • vuex
  • localStorage sessionStorage
父组件向子组件传参
  • 父组件向子组件传参: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
            }
          }
        
组件相互传参(Eventbus)

发布订阅( o n 、 on、 onemit)

  • b—>c传数据

    • c向事件池中添加方法(自定义方法):$on
    • b执行方法把参数传过去:$emit
  • 第一步:全局的main.js中

    • // 创建一个全局的EventBus
        let EventBus=new Vue();
      //将EventBus放在Vue的原型上,通过$bus来调用
        Vue.prototype.$bus=EventBus;
      
      • 作用:将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[接收])

  • 第一步:祖先要使用provide方法传参,不是写在methods里面,与methods同级
  • 第二步:后代使用inject属性接受祖先中的参数,inject是data中的数据,是数组类型
    • 注意点:因为inject是数组类型,所以它符合如果数据项不是对象类型,则不做劫持,如果数据项是对象,则这个对象中的属性会做劫持。
vue实例属性传参

( p a r e n t 、 parent、 parentchildren[n]、 r o o t 、 root、 rootrefs)

  • vue的实例中存在一些属性能够获取不同关系的元素,每调用一次组件(<A></A>)都会产生一个私有的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"
      • 如果ref定义在DOM标签中this.$refs.xxx获取的是DOM对象
      • 如果ref定义在子组件标签中this.$refs.xxx获取的是子组件的vm实例

父组件绑定在子组件标签中的事件,是无法触发的,如何解决?

@xxx.native: 监听组件根元素的原生事件。

  • 例子<my-component @click.native="onClick"></my-component>

  • 原理:在父组件中给子组件绑定一个==原生(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

  • 重点:父组件更新默认不会触发子组件更新,但是**如果子组件中绑定调用了父组件的数据aaa,父组件的aaa数据更新触发重新渲染时,使用aaa数据{{$parent.aaa}}的子组件也会触发更新**

vuex

  • vuex:状态管理库**(公共大仓库)**,集中式存储管理所有组件的状态值,相当于把所有Vue组件的状态数据放到一个公共数据库中,任何一个组件修改了公共数据库中的状态值,其他使用了该状态值的组件都会跟着刷新渲染这个值

  • Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源“而存在。这也意味着,每个应用将仅仅包含一个 store 实例

  • vuex的作用

    • 1、vuex是专为Vue.js应用程序开发的状态管理模式(集中式存储)—>公共仓库
    • 2、缓存 keep-alive
      • localStorage:本地存储,刷新有数据、数据永久
      • sessionStorage:会话存储,刷新有数据、关闭页面没有数据
      • vuex:不刷新,不关闭页面,就会保存以前的数据,刷新就不存在数据
  • vuex:“单向数据流”理念

    • image-20211210181113883
  • vuex的异步函数执行的过程:

    • image-20211210100524034

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
  • 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
  • getters:相当于vue中的computed

  • 作用:在vue组件获取state状态值之前,对状态值做格式化,再传给vue组件

  • 参数

    • state:【默认参数】指向的是vuex的实例store里的state
    • getters:【默认参数】指向的是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
  • mutations:相当于vue中的methods(同步)

  • 要点:mutations中的函数必须是同步的,不能放异步方法

  • 作用:vue组件不能直接修改store大仓库中的状态值,(会导致页面与仓库不同步),如果想做到所有组件数据,与store内state状态值同步,就要经过mutations中的方法来修改

  • 参数

    • state:【默认参数】指向当前vuex实例store的state对象
    • payload:载荷,【第二个参数】用来接收组件传给函数的参数,如果想传多个数据,载荷应该是一个对象
    • 第三个及以后的参数不会发送给mutations里的方法
  • 用法

    • 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
  • actions:相当于vue中的methods(异步)

  • 作用:mutation只能存储同步函数,actions就是将同步函数封装为异步函数

    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作。
  • 参数

    • 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>
        
modules
  • 作用:减少单一状态树的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属性中的方法

mapState()
  • 在真实项目中我们不会使用{{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()

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()
  • 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()
  • 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缓存

  • 问题:没有用缓存之前,我们每次打开页面都会发送请求

  • 目标:应用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
        	<template>
        	<div class="three">
        		{{this.$store.state.list}}
        	</div>
        </template>
        
        <script>
        	export default{
        		name:"Three",
        		data(){
        			return{
        			}
        		},
        		created(){
        			if(!this.$store.state.list){//只要list是null,表示没缓存,向服务器端发请求
        				this.getData();
        			}
        			//否则直接调用list即可
        		},
        		methods:{
        			async getData(){
        				let result= await this.$api.getTaskList();
        				console.log(result);
        			}
        		}
        	}
        </script>
        
        <style>
        </style>
        
        

vuex的使用步骤

  • 第一步:安装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">add</button>//修改仓库中的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++
                          }
                        }
              })
      	
      
    • image-20211210104851749

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值