vue2基础知识

前端工程化与webpack

模块化(js的模块化、css的模块化、资源的模块化)

组件化(复用现有的ui结构、样式、行为)

规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git分支管理)

自动化(自动化构建、自动部署、自动化测试)

webpack提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端JavaScript的兼容性,性能优化等强大功能

webpack配置

不需要手动配,需要了解原理,实际开发中会使用命令行工具(cli)一键生成webpack的项目

webpack中的默认约定

在webpack4.x和5.x的版本中,有如下的默认约定

  • 默认的打包入口文件为src -> index.js
  • 默认的输出文件路径为dist ->main.js

自定义打包入口与出口

在webpack.config.js配置文件中通过entry节点指定打包的入口。通过output节点指定打包的出口

代码如下:

const path=require(‘path’) //导入专门处理操作路径的模块

module.exports={

​ entry:path.join(_dirname ,‘./src/index.js’) //打包入口文件的路径

​ output: {

​ path: path.join(_dirname,‘./dist’)

} //输出文件的存放路径

​ filename:‘bundle.js’ //输出文件的名称

}

webpack插件

安装webpack 和wabpack-cil

webpack-dev-serve插件

在package.json中script中改为"webpack server"

webpack-dev-server 在编译之后不会产生任何输出文件。而是将webpack输出的 bundle 文件保留在内存中,并将它们当作挂载在服务器根路径上的真实文件来提供服务。实时打包更新

类似node.js用的nodemon工具

每当修改了源代码,webpack会自动进行项目的打包和构建

html-webpack-plugin插件

webpack中HTML插件(类似于一个模板引擎插件)

可以通过此插件 自定制index.html页面的内容

在webpack.config.js配置文件中

引用

const HtmlPlugin = require(‘html-webpack-plugin’)

const htmlPlugin = new HtmlPlugin({

​ template: ‘./src/index.html’,

​ // 指定复制出来的文件名和存放路径

​ filename: ‘./index.html’,

})

调用

在moudle.exports里

entry: path.join(__dirname, ‘./src/index1.js’),

​ output: {

​ path: path.join(__dirname, ‘dist’),

​ filename: ‘bundle.js’

​ },

​ // 插件的数组,将来webpack在运行时,会加载并调用这些插件

​ plugins: [htmlPlugin],

作用:把指定的页面复制到根目录,还会自动引入打包bundle.js文件

devServer节点

在webpack.config.js配置文件中,通过devServer节点对webpack-dev-server插件进行更多的配置

devServer: {

​ open: true, //初次打包完成后,自动打开浏览器

​ host: ‘127.0.0.1’.//实时打包所使用的主机地址

​ potr: 80, //实时打包所使用的端口号

}

webpack中的loader

webpack默认只打包处理以.js后缀名结尾的模块。其他非.js后缀名结尾的模块,webpack默认处理不了,需要调用loader加载器才可以正常打包,否则会报错!

loader加载器的作用:协助webpack打包处理特定的文件模块。比如:

  • css-loader 可以打包处理.css相关的文件

    npm i [email protected] [email protected] -D

    在webpack.config.js的module ->rules数组中 ,添加loader规则如下:

    module:{

    ​ rules: [

    ​ {test:/.css$/, use: [‘style-loader’,‘css-loader’]}

    ]

    }

    其中,test表示匹配的文件类型,use表示对应要调用的loader

    注意:

    • use数组中指定的loader顺序是固定的
    • 多个loader的调用顺序是:从后往前调用
  • less-loader 可以打包处理.less相关的文件

    npm i [email protected] [email protected] -D命令

    在webpack.config.js的module ->rules数组中 ,添加loader规则如下:

    module:{

    ​ rules: [

    ​ {test:/.css$/, use: [‘style-loader’,‘css-loader’,‘less-loader’]}

    ]

    }

  • babel-loader 可以打包处理webpack无法处理的高级js语法

优化图片请求

用户请求数据会先请求html标签,再请求照片,

解决方案: 精灵图或者base64转换成图片编码(缺点是体积会变大,适合小logo)

打包处理样式表中的url路径相关的文件

运行npm i [email protected] [email protected] -D 命令

在webpack.config.js的module ->rules数组中 ,添加loader规则如下:

module:{

​ rules: [

​ //如果需要调用的loader,只有一个,则只传递一个字符串也行,多个loader则必须指定数组

​ {test:/.jpg|png|gif$/, use: ‘url-loader?limit=22229’},

]

}

其中?之后的是loader的参数项:

  • limit用来指定图片的大小,单位是字节(byte)
  • 只有<=limit大小的图片,才会被转换成base64格式的图片

打包处理js文件中的高级语法

webpack只能打包处理一部分的高级的JavaScript语法,对于那些webpack无法处理的高级js语法,需要借助babel-loader进行打包处理

如下webpack解决不了

//定义了名为info的装饰器

function info(target){

//为目标添加静态属性 info

​ target.info = ‘Person info’

}

为person类应用info装饰器

@info

class Person{}

console.log(Person.info)

解决方案:

npm i [email protected] @babel/[email protected] @babel/[email protected] -D

在webpack.config.js的module ->rules数组中 ,添加loader规则如下:

module:{

​ rules: [

​ {test:/.js$/, use: ‘babel-loader’,exclude: /node_modules/}

]

}

在项目根目录下创建babel.config.js.的配置文件 ,定义Babel的配置项如下

module.exports = {

​ //声明babel可用的插件

​ plugins:[[‘@babel/plugin-proposal-decorators’,{legacy:true}]]

}

配置webpack的打包发布

在package.json文件的scripts节点下,新增bulid命令

“scripts”:{

​ “dev”: “webpack serve”, //开发环境中,运行dev命令 存在内存

​ “bulid”: “webpack --mode production” //项目发布时,运行bulid命令 存在物理磁盘 后端来拿

}

–mode 是一个参数项,用来指定webpack的运行模式,production代表生产环境,会对打包生成的文件进行代码压缩和性能优化

注意:用过–mode指定的参数项,会覆盖webpack.config.js中的mode选项

把JavaScript文件统一生成到js目录中

在webpack.config.js配置文件的output节点中,进行配置

output:{

​ path:paht.join(_dirname,‘dist’)

​ filename:‘js/bundle.js’

}

把图片文件统一生成到image目录中

修改webpack.config.js中的url-loader配置,新增outputPath选项即可指定图片文件的输出路径

{

​ text:/.jpg|png|gif$/,

​ use: ‘url-loader?limit=470&outputPath=images’

}

自动清除dist目录下的旧文件

//安装插件

clean-webpack-plugin

//导入

const {CleanWebpackPlugin}=require(‘clean-wabpack-plugin’)

const cleanPlugin = new CleanWebpackPlguin()

//把创建的cleanPlugin插件实例对象。挂载到plugins节点

plugins:[htmlPlugin,cleanPlugin]

Source Map

是一个信息文件,里面存储着位置信息,Source Map文件中存储着压缩混淆后的代码,所对应的转换前的位置

出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大地方便后期的调试

开发阶段

在webpack.config.js中添加如下的配置,即可保证运行时报错的行数与源代码的行数一直保持一致,也展示源码

module.exports = {

​ mode: ‘development’,

​ //eval-source-map 仅限在"开发模式"下使用,不建议在生产模式下使用

​ //此选项生产的Source Map能够保证"在运行时报错的行数"与"源代码的行数"保证一致

​ devtool: eval-source-map’,

}

在生产环境下,如果省略了devtool选项,则最终生产的文件中不包含Source Map。这能够防止原始代码通过Source Map的形式暴露

实际发布

建议设置为devtool: ‘nosources-source-map’,或者直接关闭Source Map

定位行数且暴露源码

devtool: ‘source-map’,

会产生.map文件

@表示源代码目录

建议使用@从外往里查找,不要使用…/从里往外查找

用前要配置,不用手动配

在webpack.config.js下

module.exports = {

​ resolve: {

​ alias:{

​ ‘@’: path.join(__dirname,‘./src/’)

}

}

}

ES6模块化

在ES6模块化规范之前。JavaScript社区尝试并提出了AMD、CMD、CommonJS等模块化规范

但存在一定的差异性和局限性,并不是浏览器与服务器通用的模块化标准

AMD和CMD适用于浏览器端的JavaScript模块化

CommonJS适用于服务器端的JavaScript模块化

ES6模块化是浏览器端与服务器端通用的模块化开发规范

ES6模块化规范中的定义:

  • 每个js文件都是一个独立的模块
  • 导入其他模块成员使用import关键字
  • 向外共享模块成员使用export关键字

在node.js中体验es6模块化

配置
  • 确保安装v14.15.1或更高版本的node,js
  • 在package.json的根节点中添加"type":"module"节点
基本语法
  1. 默认导出与默认导入

    导出: export default {默认导出的成员}

    导入:import 接收名称 from ‘模块路径’

    注意事项:

    ​ 每个模块中,只允许使用唯一的一次export default, 否则会报错

    ​ 接收名称任意取值,但不能以数字开头

  2. 按需导出与按需导入

    ​ export let s1=‘aaa’

    ​ export function say() {}

    ​ import {s1 as s11} from ‘模块路径’

    注意事项:

    ​ 每个模块中可以多次按需导出

    ​ 按需导入的成员名称必须和按需导出的名称保持一致

    ​ 按需导入时,可以使用as关键字进行重命名

    ​ 按需导入可以和默认导入一起用

  3. 直接导入并执行模块中的代码

    只想执行某个模块中的代码,并不需要得到模块中向外共享的成员

    for( let i =0; i<3;i++){

    ​ console.log(i)

    }

    import ‘路径’

Promise

回调地狱

多层回调函数的相互嵌套,就形成了回调地狱

setTimeout(()=>{

​ console.log(‘延时1秒后输出’)

​ setTimeout(() => {

​ console.log (‘延时2秒后输出’)

​ setTimeout(() =>{

​ console.log(‘延时3秒后输出’)

}。3000)

},2000)

},1000)

缺点:

  • 代码耦合性太强。难以维护
  • 大量冗余的代码相互嵌套,代码的可读性差

promise基本概念

  1. Promise是一个构造函数
    • const p = new Promise()
    • new出来的Promise实例对象,代表一个异步操作
  2. Promise.prototype上包含一个.then()方法 原型对象
    • 每一次new Promise()构造函数得到的实例对象
    • 都可以通过原型链的方式访问到.then()方法,例如p.then()
  3. .then()方法用来预先指定成功和失败的回调函数
    • p.then(成功的回调函数,失败的回调函数)
    • p.then(result =>{}, error =>{})
    • 调用.then()方法时,成功的回调函数是必选的、失败的回调函数是可选的

基于回调函数按顺序读取文本内容

造成回调地狱

基于then-fs读取文件内容

node.js官方提供的fs模块仅支持以回到函数的方式读取文件,不支持Promise的调用方法。因此。需要安装

then-fs这个第三方包,从而支持基于Promise的方式读取文件的内容

基本使用

调用then-fs提供的readFile()方法,可以异步读取文件的内容,它的返回值是Promise的实例对象。

因此可以调用.then()方法为每个Promise异步操作指定成功与失败之后的回调函数

import thenFs from ‘then-fs’

thenFs.readFile(‘./files/1.txt’,‘utf-8’),then(r1=>{console.log(r1)},err =>{console.log(err1.message)}}

thenFs.readFile(‘./files/2.txt’,‘utf-8’),then(r2=>{console.log(r1)},err =>{console.log(err2.message)}}

thenFs.readFile(‘./files/3.txt’,‘utf-8’),then(r3=>{console.log(r1)},err =>{console.log(err3.message)}}

**注意:**上述的代码无法保证文件的读取顺序,需要进一步改进

基于promise按顺序读取文件的内容

Promise支持链式调用,从而解决回调地狱的问题

thenFs.readFile(‘./files/1.txt’,‘utf8’)

​ .then((r1) => {

​ console.log(r1)

​ return thenFs.readFile(‘./files/2.txt’,‘utf8’)

})

.then((r2) => {

​ console.log(r2)

​ return thenFs.readFile(‘./files/3.txt’,‘utf8’)

})

.then((r3) => {

​ console.log(r3)

})

.catch(err =>{

​ console.log(err.message)

})

如果不希望前面的错误导致后续的.then无法正常执行,则可以.catch的调用提前

thenFs.readFile(‘./files/1.txt’,‘utf8’)

.catch(err =>{

​ console.log(err.message)

})

​ .then((r1) => {

​ console.log(r1)

​ return thenFs.readFile(‘./files/2.txt’,‘utf8’)

})

.then((r2) => {

​ console.log(r2)

​ return thenFs.readFile(‘./files/3.txt’,‘utf8’)

})

.then((r3) => {

​ console.log(r3)

})

promise.all()方法

会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制 )

const promiseArr = [

​ thenFs.readFile(‘./files/1.txt’,‘utf-8’),

​ thenFs.readFile(‘./files/2.txt’,‘utf-8’),

​ thenFs.readFile(‘./files/3.txt’,‘utf-8’)

]

Promise.all(promiseArr)

.then(([r1,r2,r3]) => {

​ console.log(r1.r2.r3)

})

.catch(err => {

​ console .log(err.message )

})

Promise.race()方法

会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)。

const promiseArr = [

​ thenFs.readFile(‘./files/1.txt’,‘utf-8’),

​ thenFs.readFile(‘./files/2.txt’,‘utf-8’),

​ thenFs.readFile(‘./files/3.txt’,‘utf-8’)

]

Promise.race(promiseArr)

.then(([r1,r2,r3]) => {

​ console.log(r1.r2.r3)

})

.catch(err => {

​ console.log(err.message )

})

自定义Promise封装读文件的方法

方法的封装要求

  1. 方法的名称要定以为getFile

  2. 方法接收一个形参fpath,表示要读取的文件路径

  3. 方法返回值为Promise实例对象

    import fs from ‘fs’

function getFile(fpath){

​ return new Promise(function(resolve,reject){ //如果()里没有function那只是创建了一个形式上的异步操作

​ fs.readFile(fpath,‘utf8’,(err,dataStr ) => {

​ if(err) return reject(err)

​ resolve(dataStr)

})

})

}

getFile(‘./files/1.txt’).then((r1)=>{console.log(r1)},(err) =>{console.log(err.message)})

或者

getFile(‘./files/1.txt’).then((r1)=>{console.log(r1)})

.catch((err) => console.log(err.message))

async/await

是es8引入的新语法,用来简化Promise异步操作。在async/await出现之前,开发者只能用.then()方法处理异步

async function getAllFile() {

​ const r1 = await thenFs.readFile(‘./files/1.txt’,‘utf8’) //r1会获取到文件内容

​ console.log(r1)

​ const r2 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)

​ console.log(r2)

​ const r3 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)

​ console.log(r3)

}

getAllFile()

注意事项:

​ 如果在function中使用了await,则function必须被async修饰

​ 在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行

​ console.log(‘A’)

​ async function getAllFile() {

​ console.log(‘B’)

​ const r1 = await thenFs.readFile(‘./files/1.txt’,‘utf8’)

​ const r2 = await thenFs.readFile(‘./files/2.txt’,‘utf8’)

​ const r3 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)

​ console.log(r1,r2,r3)

​ console.log(‘D’)

}

getAllFile()

console.log(‘C’)

输出结果:

A

B

C

111 222 333

D

EventLoop(事件循环)

JavaScript是单线程的语言

如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题

为了防止此问题,JavaScript把待执行的任务分为了两类

同步任务

  • 又叫做非耗时任务,指得是在主线程上排队执行的那些任务
  • 只有前一个任务执行完毕,才能执行后一个任务

异步任务

  • 又叫做耗时任务,异步任务由JavaScript委托给宿主环境(浏览器或者node.js)进行执行
  • 当异步任务执行完成后,会通知JavaScript主线程执行异步任务的回调函数

例子:

import thenFs from ‘then-fs’

console.log(‘A’)

thenFs.readFile(‘./files/1.txt’,‘utf8’).then(dataStr => {

​ console.log(‘B’)

})

setTimeout(() =>{

​ console.log(‘C’)

},0)

console.log(‘D’)

输出

ADCB

  • AD属于同步任务,会根据代码的先后顺序依次被执行
  • CB属于异步任务,它们的回调函数会被加入到任务队列中,等待主线程空闲时再执行

宏任务与微任务

JavaScript把异步任务又做了进一步的划分

  • 宏任务(macrotask)

    • 异步Ajax请求
    • setTimeout、setinterval
    • 文件操作
    • script 的代码
    • 其他宏任务
  • 微任务(mincrotask)

    • Promise.then、.catch和.finally
    • process.nextTick
    • 其他微任务

执行顺序

ES6模板字符串

${变量}

vue

用于构建用户界面的前端框架

特性:

1.数据驱动视图

在使用了vue的页面中,vue会监听数据的变化,从而自动重新渲染页面的结构。

注意:数据驱动视图是 单向的数据绑定

2.双向数据绑定

在网页中,form表单负责采集数据,Ajax负责提交数据

在填写表单时,双向数据绑定可以辅助开发者在不操作DOM的前提下,自动把用户填写的内容同步到数据源中

  • js数据的变化,会自动渲染到页面上
  • 页面上表单采集的数据发生变化的时候,会被vue自动获取到,并更新到js数据中

MVVM

是vue实现数据驱动视图和双向数据绑定的核心原理,它把每个HTML页面都拆成如下三个部分:

View 表示当前页面所渲染的DOM结构

Model 表示当前页面渲染时所依赖的数据源(假数组,ajax请求过来的数组)

ViewModel 表示vue的实例,是MVVM的核心

版本

vue3新增功能例如:

组合式API、多根节点组件、更好的Typescript支持等

废弃的旧功能:

过滤器、不再支持 o n , on, on,off和$once实例方法等

基本使用

  1. 导入vue.js的script脚本文件
  2. 在页面中声明一个将要被vue所控制的DOM区域
  3. 创建vm实例对象(vue实例对象)

vue指令与过滤器

指令是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构,有六大类

内容渲染指令

辅助开发者渲染DOM元素的文本内容

v-text
  • 会覆盖元素内部原有的内容
  • 只能渲染纯文本内容,不能渲染html
{{}}
  • {{}}专业名称是插值表达式
  • 只能渲染纯文本内容,不能渲染html
v-html
  • 能渲染html标签
属性绑定指令
v-bind

v-bind:placeholder=“tips”

v-bind可以简写成: // :placeholder=“tips”

也支持JavaScript表达式的运算

例子:

这是一个div

事件绑定指令
v-on

例子: +n

在vm的实例对象中

添加 methods: {

​ add: function (n) { //:funtion 可省略 add(n){}

​ console.log(‘ok’)

​ //vm.count +=n

​ this.count +=n

​ }

​ }

可以简写成@click

如果不传参,可以用e来接收,e是事件的对象,可以修改e,target.style.属性

$event

vue的内置变量,就是原生DOM的事件对象e

例子:+1

add: function (n, e) {

​ // vm.count += 1

​ this.count += 1

​ if (this.count % 2 == 0) {

​ e.target.style.backgroundColor = ‘red’

​ } else {

​ e.target.style.backgroundColor = ‘’

​ }

​ }

事件修饰符
事件修饰符 说明
.prevent 阻止默认行为(例如:阻止a链接跳转,阻止表单的提交等)
.stop 阻止事件冒泡

点击链接进百度页面

按键修饰符

​ //只有在按下Esc时调用’vm.clearInput()’

​ //只有在按下Enter时调用’vm.submit()’

双向绑定指令
v-model

用来辅助开发者不操作DOM的前提下,快速获取表单的数据(用于表单或者select)

v-model指令的修饰符
修饰符 作用 示例
.number 自动将用户的输入值转为数值类型
.trim 自动过滤用户输入的首尾空白字符
.lazy 在"change"时而非input 时更新
条件渲染指令

用来辅助开发者按需控制DOM的显示和隐藏

v-if

动态创建和移除DOM来显示和隐藏,适用于默认不需要展示,而且后期很可能也不需要展示,v-if性能更好

v-show

用过display属性来控制显示和隐藏,适用于频繁显示隐藏的情况,性能更好

在实际开发,绝大多数情况下,不考虑性能,直接使用v-if

列表渲染指令

用来辅助开发者基于一个数组渲染一个列表结构

v-for

需要使用item in items形式的特殊语法

items是待循环的数组

item是被循环的每一项

v-for=“(item,idex) in items” //另一种格式

官方建议:只要用到了v-for,需要绑定一个:key属性

key的注意事项
  • key的值只能是字符串或数字类型
  • key的值必须具有唯一性(:key的值不能重复)
  • 建议把数据项id属性的值作为key的值(id属性具有唯一性)
  • 使用index的值当作key的值没有任何意义(因为inde的值不具有唯一性)
  • 使用v-for指令一定要指定key的值(即提升性能、又防止列表状态紊乱)
过滤器(vue3已删除)

Filter,本质上是一个函数,常用于文本的格式化,可以用在:插值表达式和v-bind属性绑定

过滤器应该被添加到js表达式的尾部,由"管道符"进行调试

例子:

{{message | Filter}}

v-bind:id=“Id | Filter”

(与data同级)filters:{

​ //形参val,永远都是"管道符"前面的值

​ Fliter(val){

​ //一定要有返回值

​ return

}

}

私有过滤器

定义在vm实例内,代码如上

全局过滤器

vue.filter(‘参数1’, function(str) {

​ return str.charAt(0).toUpperCase() + str.slice(1) +‘~~’

})

参数1: 是全局过滤器的名字

参数2: 是全局过滤器的处理函数

注意:

​ 如果全局过滤器和私有过滤器的名字相同,此时按照"就近原则",调用私有过滤器

其他用法

多个过滤器使用

message|xxx|yyy|jjj

过滤器可以传参

message |filterA(arg1,arg2)

vue.flilter(‘filterA’,(message,arg1,arg2)) =>{

}

vue3,官方建议使用计算属性或方法代替过滤器功能

watch 侦听器

针对数据的变化做特定的操作

v-model=‘username’

data: {

​ username: ‘admin’

}

watch:{

​ //侦听器本质是一个函数。要监视哪个数据的变化,就把数据名作为方法名即可

​ //newVal是’变化后的新值’,oldVal是’变化之前的旧值’

​ username(newVal,oldVal){

}

}

侦听器的格式
  1. 方法格式的监听器(上述代码)

    • 缺点1:无法在刚进入页面的时候,自动触发!!!
    • 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器
  2. 对象格式的监听器

    • 好处:可以通过immediate选项,让侦听器自动触发
    	watch: {
    ​	username: {
    	//handler是固定写法handler(newVal,oldVal){
    
    ​	console.log(newVal,oldVal)
    
    },
    	//表示页面初次渲染好之后,就立即触发当前的watch侦听器
    ​	immediate:true    //默认值是false
    
    }
    

//侦听的值为对象时

watch: {

​	info: {handler(newVal){

​	console.log(newVal,oldVal)

},

​	deep: true  // 开启深度监听,只要对象中任何一个属性变化了,都会触发"对象的侦听器"//如果要侦听的是对象的子属性,则必须包裹一层单引号 'info.username'(){

​	console.log

}

}
计算属性

通过一系列运算之后,最终得到一个属性值

这个动态计算出来的属性值可以被模板结构或methods方法使用

//所有的计算属性都要定义到computer节点下

//计算属性在定义的时候,要定义成"方法格式" 调用时是属性

computed:{

​ rgb(){ return rgb(${this.r},${this.g},${this,b}) }//es6模板字符串

},

methods: {

​ show(){ console.log(this.rgb)}

}

好处:

  1. 实现代码的复用
  2. 只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值
  3. 计算属性有缓存,条件没变多次调用只会计算一次。可以节约算力资源
axios

axios 是一个专注于网络请求的库

get请求
1.   //调用axios	方法得到的是promise对象

document.querySelector("#btnpost").addEventListener('click', async function(){
    //{}解构赋值,从axios封装的大对象中,把data属性结构出来,使用:重命名
    const {data : res} = await axios({
        method:'POST',
        url: 'http://www.liulongbin.top:3006/api/getbooks',
        params:{
            id=1
        }
    })
   // console.log(data.data)
    console.log(res.data)
})
post请求
1. document.querySelector("#btnpost").addEventListener('click', async function(){
    //{}解构赋值,从axios封装的大对象中,把data属性结构出来,使用:重命名
    const {data : res} = await axios({
        method:'POST',
        url: 'http://www.liulongbin.top:3006/api/getbooks',
        data: {
            name:'zs',
            age:20
        }
    })
   // console.log(data.data)
    console.log(res.data)
})

axios请求
<button id="btnGET">GET</button>

    <button id="btnPOST">POST</button>
    <script src="./lib/axios.js"></script>
    <script>
        document.querySelector('#btnGET').addEventListener('click', async function () {
            // axios.get('url地址',{
            //GET参数
            //params:{}
            // })
            const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
                params: { id: 1 }
            })

            console.log(res)
        })
        document.querySelector('#btnPOST').addEventListener('click', async function () {
            // axios.post('url',{//post请求体数据})
            const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
            console.log(res)
        })

    </script>

单页面应用程序

简称SPA,指的是一个web网站中只有唯一的一个html页面,所有的功能与交互都在这唯一的一个页面完成

vue-cli

vue-cli是vue开发的标准工具,它简化了程序员基于webpack创建工程化的vue项目的过程

安装
 npm i  -g @vue/cli
 vue create 项目名称   

鼠标在cmd窗口乱画会冻结导致暂停,解冻:点击cmd的窗口 +ctrl+c

src目录的构成
1 assets 文件夹:存放项目中用到的静态资源文件,例如:css样式表、图片资源等等
2 components 文件夹:程序员封装的、可复用的组件,都要放到此目录下
3 main.js 是项目的入口文件。整个项目的运行,要先执行main.js
4 App.vue 是项目的根组件

在工程化项目中,vue通过main.js把App.vue渲染到index.html的指定区域中(替换)

el: ‘#app’, 与.$mount(‘#app’)作用一致

vue组件

组件化开发:根据封装的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发和维护

vue是一个支持组件化开发的框架

vue中规定,组件的后缀名是.vue。App.vue本质上就是一个vue的组件

组件分类

智能组件(数据获取,数据过滤…)

木偶组件(渲染视图)

三个组成部分
  • template -> 组件的模板结构 (必须有)

    只能有一个根节点(最外面只能有一个div)

  • script -> 组件的JavaScript行为(可选)

    
    
  • style -> 组件的样式(可选)

render函数中,渲染的是哪个.vue组件,那么这个组件就叫做"根组件"

组件关系

组件再被封装好后,彼此之间是相互独立,在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系

使用组件的三个步骤
  1. 使用import语法导入需要的组件

  2. 使用components节点注册组件

  3. 以标签形式使用刚才注册的组件(使用的时候就创建了一个对象实例)

@配置文件

在vscode里的settings.json中添加

"path-autocomplete.extensionOnImport": true,
    "path-autocomplete.pathMappings": {
        "@": "${folder}/src"
    },

在vue.app就可以用@引用文件

通过components节点注册是私有子组件

子组件不能用在别的组件

注册全局组件

在vue项目的main.js入口文件中,通过Vue.components()方法,可以注册全局组件

import xxx from “yyy”

Vue.components(“变量名”,xxx)

组件的props

props是组件的自定义属性,在封装通用组件的时候,合理使用props可以极大提高组建的复用性

可定义成数组或者对象

export default {
//是自定义属性,允许使用者通过自定义属性,为当前组价指定初始值
    props:['init']
}

//这是字符串"9",想转换成数字, :init=“9”

props是只读的

不能直接修改props的值

解决方案,把props 赋值到data里

props:['init']
data(){
    return{this.init}
}
props里的值
export default {
    props:{
        init:{
        	//用default 属性定义属性的默认值
            default:0
            //用type 属性定义属性的值类型
            //如果传递过来的值不符合此类型,则会在终端报错
            //可用[Number,String]定义多个类型
            type:Number
            //必填项校验
            require:true
        }
        //在使用组件中,如果某个属性名是小驼峰形式,cmtCount,则绑定属性值时建议改写成
        连字符格式,cmt-count
    }
}
组件样式冲突

默认情况下,写在.vue组件中的样式会全局生效,因此会造成多个组件之间的样式冲突问题

解决

在style标签里添加 scoped属性

原理:scoped会给template里标签添加 data-v-n属性

当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到/deep/

/deep/ h3

每个.vue都交给vue-template-compiler包编译成js

组件的生命周期

生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个过程,强调的是一个时间段

生命周期函数:是由vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

在created阶段,会使用created函数调用methods中的方法,发起ajax请求拿数据,并且把请求到的数据转存到data中,供template模板渲染的时候使用

method(){
    const xml = new 
},
creater(){
    this.initbooks()
}

在数据发生变化之后,为了能够操作最新的DOM结构,必须把代码写到updated生命周期函数中

组件之间的数据共享
父子关系

父组件向子组件共享数据,需要使用自定义属性

//父组件


data(){
    return {
        message:'hello vue.js',
        userinfo:{name:'zs',age:20}
    }
}
//子组件


props:['msg','user']

子组件向父组件共享数据,使用自定义事件

//子组件
 
  methods: {
    add() {
      // 让子组件的count值自增
      this.count += 1;
      // 把自增的结果,传给父组件
      this.$emit("numchange", this.count);
    },
  },
//父组件
 

App 根组件 --- {{ countFromSon }}

methods: { // 获取子组件传递过来的数据 getNewCount(val) { console.log("numchange 事件被触发了!", val); this.countFromSon = val; }, },
兄弟组件之间的数据共享

在vue2中,兄弟组件之间数据共享的方案是EventBus。

EventBus的使用步骤

  • 闯将eventBus.js模块,并向外共享了一个vue实例对象
  • 在数据发送方,调用bus.$emit(‘事件名称’,要发送的数据)方法触发自定义事件
  • 在数据接收方,调用bus.$on(‘事件名称’,事件处理函数)方法注册一个自定义事件
//发送方
 
import bus from './eventBus.js'
data(){
return {
      str: `黑云压城城欲摧,渚青沙白鸟飞回。借问酒家何处是,半江瑟瑟半江红!`,
    }
    },
     methods: {
    send() {
      // 2. 通过 eventBus 来发送数据
      bus.$emit('share', this.str)
    }
  }
  //接收方
  

{{ msgFromLeft }}

import bus from "./eventBus.js"; data() { return { msgFromLeft: "", }; }, created() { // 2. bus.$on("share", (val) => { console.log("", val); this.msgFromLeft = val; }); },
ref引用

ref用来辅助开发者在不依赖于jQuery的情况下,获取的DOM元素或组件的引用

每个vue的组件实例中,都包含一个$refs对象,里面存放着对应的DOM元素或组件的引用。

默认情况下,组建的$refs指向一个空对象

《h3 ref=“myh1”>ref的名字不能重复

this.$refs.myh1.style.color=‘red’

父组件可以通过ref控制子组件

例子

​ //有Left子组件,com+子组件名

《Left ref=“comLeft”>

onReset(){

​ this.$refs.comLeft.方法()

}

this.$nextTick(cb)方法

此方法会把cb回调退出到下一个DOM更新周期止之后执性。也就是等组件的dom更新完成之后,在执行cb回到函数,从而确保cb回调函数可以操作到最新的DOM元素

this.$nextTick(() =>{

this.$refs.iptRef.focus()

})

数组的方法

some

arr.some((item,index)=>{
	
​	if(item=="){
    //再找到对应的项之后,可以通过return true固定的语法,来终止some循环
    	return true
}

})

ever

//需求:判断数组中是否被全选了 (例子:全选)
const result =arr.ever(item => item.state)

reduce

//需求:把购物车数组中,已勾选的物品,总价累加起来
方法一:
let  amt=0
arr.filter(item => item.state).forEach(item =>{
    amt +=item.price * item.count
})
方法二:
//arr.filter(item => item.state).reduce((累加的结果,当前循环项)={},初始值)
const result = arr.filter(item => item.state).reduce((amt,item)={
    return amt +=item.price * item.count	
},0)
//简写:
const result = arr.filter(item => item.state).reduce((amt,item)=> amt+=item.price * item.count),0)

动态组件

动态组件指的是动态切换组件的显示与隐藏

如何实现动态组件渲染

vue提供了一个内置组件,专门用来实现动态组件的渲染

data(){
//1.当前要渲染的组件名称
    return {comName:'Left'}
}
//2.通过is属性,动态指定要渲染的组件

//3.点击按钮,动态切换组件的名称


使用keep-alive保持状态

上述代码,切换Right时,Left会被销毁,切换回来时会重新创建,但之前的数据会被初始化




keep-alive对应的生命周期函数

当组件被缓存的时候,会自动触发组件的deactivated生命周期函数

当组件被激活的时候,会自动触发组件的activated生命周期函数

include属性

用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号隔开



exclude属性

用来指定: 哪些组件不需要被缓存



注意:

  • 如果不写属性,默认全部缓存
  • 两个属性不能一起写,只能写其中一个

命名:

在父组件中"声明组件"的时候,没有在子组件中指定name名称,则组件的名称默认就是在components下注册的名称

对比:

  1. 组件的"注册名称"的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中

  2. 组件声明的时候的"name"名称的主要应用场景:结合标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称

插槽

插槽(Slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽(相当于子传父)

vue官方规定:每一个slot插槽,都要有一个name名称

如果省略了slot的name属性,则有一个默认名称叫做default

《slot name=“default”>

v-slot指令
指令缩写是#

#名字

如果要放默认内容,放到slot标签内

具名插槽

有自己的name名称就是个具体插槽,为了告诉用户

作用域插槽

为预留的slot提供属性对应的值,这种用法叫做"作用域插槽"

官方标准: obj改为scope

自定义指令

私有自定义指令

在directives节点中声明

bind函数
directives: {
	//定义名为color的指令,指向一个配置对象
	//当指令第一次被绑定到元素上的时候,会立即触发bind函数	
	color:{
        bind(el){     //形参中的el是绑定了此指令的、原生的DOM对象 固定写法
            el.style.color='red'
        }
	}
}	
	// 调用
	

App组件

//如果v-color="blue",需要在data里定义color //另一种写法 directives: { //定义名为color的指令,指向一个配置对象 color:{ bind(el,bind){ //形参中的el是绑定了此指令的、原生的DOM对象 固定写法 el.style.color=bind.value } } } // 调用

App组件

update函数
//在DOM更新的时候,会触发update函数
update(el,bind)	{
     el.style.color=binding.value
}
函数简写

如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式

color(el,binding){
    el.style.color = binding.value
}
全局自定义

Vue.directive() 在main.js 进行声明

Vue.directive('color',function(el,binding){

​	el.style.color=binding.value
 
})

ESLint

可组装的JavaScript和jsx检查工具

详情请看ESlink中文网

配置和axios代码在vue/demo-3

路由

路由:Hash地址与组件之间的对应关系

Hash地址: #+地址

工作方式
  1. 用户点击了页面上的路由链接
  2. 导致了URL地址栏中的Hash值发生了变化
  3. 前端路由监听到了Hash地址的变化
  4. 前端路由把当前Hash地址对应的组件渲染到浏览器中
created(){
	//底层原理
    //只要当前的app组件被创建,就立即监听windows对象的onhashchange事件
    windows.onhashchange = () => {
        switch(location.hash){
            case '#/home':
            this.comName = 'Home'
            break
            case '#/movie':
            this.comName = 'Movie'
            break
            case '#/about':
            this.comName = 'About'
            break
        }
    }
}
vue-router的基本使用
安装和配置的步骤
  1. 安装vue-router包

    npm i [email protected] -S

  2. 创建路由模块

    会自动生成

    //1.导入Vue和VueRooter
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    //2.调用Vue.use()函数,把VueRouter安装为Vue的插件
    Vue.use(VueRouter)
    //3.创建路由的实例对象
    const router = new VueRouter()
    //4.向外共享路由的实例对象
    export default router	 
    
  3. 导入并挂载路由模块

    import router from '@/router/index.js'
    new Vue({
      render: h => h(App),
      //在vue项目中,要想用路由,必须把路由实例对象,通过下面的方式进行挂载
      router:router
    }).$mount('#app')
    
    
  4. 声明路由链接和占位符

//在app.use

//在router/index.js
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
const router = new VueRouter({
  //routes 是一个数组,作用:定义 hash地址 与组件之间的对应关系
  routes: [
  //路由规则
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    {path:'/About',component:About}
  ]
})
router-link 代替a标签

《a href=“#/home”>首页

《router-link to=“/home”>首页

vue-router的常见用法
重定向

指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面

redirect属性

 routes: [
  //路由规则
  //重定向的路由规则
  	{paht:'/',redirect:'/home'}	
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    {path:'/About',component:About}
  ]
嵌套路由

通过路由实现组件的嵌套展示

在父组件中

《router-link to=“/home”>首页

《router-view>

通过children属性声明子路由规则

 {
      path: '/about', component: About, children:
        [
          { path: 'tab1', component: Tab1 },
          { path: 'tab2', component: Tab2 }
        ]
    }
默认子路由

如果children数组中,某个路由规则的path值为空字符串,则这条路由规则,叫做"默认子路由"

 {
      path: '/about', component: About, children:
        [
          { path: '', component: Tab1 },
          { path: 'tab2', component: Tab2 }
        ]
 }
动态路由匹配
动态参数

动态路由值得是:是Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性

在vue-router中使用英文的冒号(:)来定义路由的参数项。

{ path: '/movie/:mid', component: Movie },
this.$route.params.mid
//可以查找到,this.$route是路由的"参数对象"
//this.$router是路由的导航对象
为路由规则开启props传参
{ path: '/movie/:mid', component: Movie,props: true },
props:['mid']
注意
  1. 在hash地址中,/后面的参数项,叫做“路径参数”

​ 在路由参数对象中,需要使用this.$route.params.query来访问路径参数

  1. 在hash地址中,?后面的参数项,叫做”查询参数“
  1. 在this.$route中 ,path只是部分路径,fullpath是完整路径
声明式导航和编程式导航

在浏览器中,点击链接实现导航的方式,叫做声明式导航

在浏览器中,调用API方法实现导航的方式,叫做编程式导航

  • location.href
vue-router中的编程式API

最常用

  • this.$router.push(‘hash地址’)

    1. 跳转到指定的hash地址,并增加一条历史记录
  • this.$router.replace(‘hash地址’)

    1. 跳转到指定的hash地址,并替换掉当前的历史记录
  • (数值n)

  1. 可以在浏览器中前进和后退

  2. go(-1)表示后退一层

    //如果后退的层数超过上限,则原地不动

go简化用法

  1. $router.back()
  2. $router.forward()

导航守卫

导航守卫可以控制路由的访问权限

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫

//创建路由实例对象
const router = new 	VueRouter({})
//调用路由实例对象的beforeEach方法,即可声明"全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发fn这个 回调函数
router.beforeEach(fn)
守卫方法的3个新参
//创建路由实例对象
const router = new 	VueRouter({})
//调用路由实例对象的beforeEach方法,即可声明"全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发fn这个 回调函数
router.beforeEach((to,from,next)=>{
    //to是将要访问的路由的信息对象
    //from是将要离开的路由的信息对象
    //next是一个函数,调用next()表示放行,允许这次路由导航
   
})
next函数的3种调用方式

当前用户拥有后台主页的访问权限,直接放行: next()

当前用户没有后台主页的访问权限,强制其跳转到登录页面: next(‘/login’)

当前用户没有后台主页的访问权限,不允许跳转到后台主页: next(false)

全局解析守卫
全局后置钩子
路由独享的守卫
组件内的守卫
如果某个组件是通过路由进行切换加载放到views文件夹,否则放components

下载vant,后面加-force

vue组件库

pc端

Element UI

View UI

移动端

Mint UI

Vant

如何使用elementui
下载

npm i element-ui -S

引入

proxy跨域代理

接口跨域问题
1.开启CORS跨域资源共享(后端)
2.通过代理解决(前端)
配置proxy

在项根目录下创建vue.config.js的配置文件

如下声明

module.exports = {

​	devServer:{

​	//当前项目在开发调试阶段

​	//会将任何未知请求(没有匹配到静态文件的请求)代理到httpxxx

​	proxy:'httpxxx',	

}

}

注意:

  1. devServer.proxy提供的代理功能,仅在开发调试阶段生效
  2. 项目上线发布时,依旧需要API接口服务器开启CORS跨域资源共享

git

开源的分布式版本控制系统

缺点:占用磁盘空间较大

优点:版本切换时非常快

三个区域

  • 工作区

  • 暂存区

  • Git仓库

三种状态

  • 已修改(modified)

    表示修改了文件,但还没将修改的结果放到存放区

  • 已暂存(staged)

    表示对已修改的文件的当前版本做了标记,使之包含在下次提交的列表中

  • 已提交(committed)

    表示文件已经安全地保存在本地的Git仓库中

配置用户信息

设置自己的用户名和邮件地址

git config --global user.name “itheima”

git config -global user.email “[email protected]

注意:如果使用了 --global选项,那么该命令只需要运行一次,即可永久生效

全局配置文件

c盘.gitconfig

检查配置信息

查看所有的全局配置项

git config --list --global

查看指定的全局配置项

git config user.name

git config user.email

获取帮助信息

可以使用git help

要想打开git config 命令的帮助手册

git help config

不想查看完整的手册

git config -h

获取Git仓库的两种方式

  1. 将尚未进行版本控制的本地目录转换为Git仓库
  2. 从其他服务器克隆一个已存在的Git仓库

在现有目录中初始化仓库

如果自己有一个尚未进行版本控制的项目目录,想要用Git来控制它,需要执行如下两个步骤:

  1. 在项目目录中,通过鼠标右键打开"Git Bath"
  2. 执行git init 命令将当前的目录转换为Git仓库

git init命令会创建一个名为.git的隐藏目录,这个.git目录就是当前项目的Git仓库,里面包含了初始的必要文件,这些文件是Git仓库的必要组成部分

工作区中文件的4种状态

工作区中的每一个文件可能有4种状态,这四种状态共分为两大类

未跟踪<------->未被Git管理 <------------>已被Git管理{未修改-已修改-已暂存}

Git操作的终极结果:让工作区中的文件都处于"未修改"的状态

检查文件的状态

git status命令查看文件处于什么状态,

在状态报告中可以看到新建的index.html文件出现在Untracked files (未跟踪的文件) 下面

未跟踪的文件意味着Git 在之前的快照(提交)中没有这些文件;git不会自动将之纳入跟踪范围,除非明确地告诉它"我需要使用Git跟踪该文件"

以精简的方式显示文件状态

使用git status 输出的转态报告很详细,但有些繁琐。如果希望以精简的方式显示文件的状态,

如下两条完全等价的命令,其中-s是 --short的简写形式:

git status -s

git status --short

??未跟踪文件(前面有红色的?)

跟踪新文件

git add 开始跟踪一个文件,所以,要跟踪index.html文件

此时再运行git.status命令,会看到index.html文件在Changes to be committed,说明已被跟踪,并处于暂存转态;

新添加到暂存区中的文件前面有绿色的A标记

提交更新

index.html文件等待被提交到Git仓库中进行保存。可以执行git commit 命令进行提交,

所有 -m选项后面是本次的提交消息,用来对提交的内容做进一步的描述:

git commit -m “新建了index.html文件”

证明工作区中所有的文件都处于"未修改"的状态,没有任务文件需要被提交

对已提交的文件进行修改

再次运行git status 和 git satus -s

文件index.html出现在Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区

  1. 注意:修改过的、没有放入暂存区 的文件前面有红色的M标记

暂存已修改的文件

需要再次运行git add

这个命令有三个功能

  • 可以用它开始跟踪新文件
  • 把已跟踪的、且已修改的文件放到暂存区
  • 把有冲突的文件标记为已解决状态

提交已暂存的文件

再次运行 git commit -m "提交信息"命令,即可将暂存区的记录的index.html的快照,提交到Git仓库中进行保存

撤销对文件的修改

把对工作区中对应文件的修改、还原成Git 仓库中保存的版本

操作的结果:所有的修改会丢失,且无法恢复!危险性比较高,请谨慎操作

git checkout – index.html

向暂存区中一次性添加多个文件

如果需要被暂存的文件个数比较多,可以使用如下的命令,一次性将所有的新增和修改过的文件加入暂存区:

git add .

  1. 今后在项目开发中,会经常使用这个命令,将新增和修改过后的文件加入暂存区

取消暂存的文件

如果需要从暂存区中移除对应的文件,可以使用如下的命令:

git reset HEAD 要移除的文件名称

git reset HEAD . 全部文件

跳过使用暂存区域

标准流程:工作区-> 暂存区 ->Git仓库

工作区->Git仓库

git commit -a -m “描述信息”

移除文件

  1. 从Git仓库和工作区中同时移除对应的文件

    git rm -f index.js

  2. 只从Git仓库中移除指定的文件,但保留工作区中对应的文件

    git rm --cached index.css

忽略文件

.gitignore的配置文件

有些文件无需纳入Git的管理,也不希望它们总出现在未跟踪的文件列表

创建名为.gitignore的配置文件,列出要忽略的文件的匹配模式

  1. 以#开头的是注释
  2. 以 / 结尾的是目录
  3. 以 / 开头防止递归 (只忽略当前目录下的/之后的文件) { bulid/ 表示忽略任何目录下名为bulid的文件夹}
  4. 以 ! 开头表示取反
  5. 可以使用glob模式进行文件和文件夹的匹配(glob指简化了的正则表示式)
glob模式
  1. 星号 *匹配零个或多个任意字符
  2. [abc]匹配任何一个列在方括号中的字符(此案例匹配一个a或匹配一个b或匹配一个c)
  3. 问号 ? 只匹配一个任意字符
  4. 在方括号中,使用短划线分割两个字符,表示所有在这两个字符范围内都可以匹配(比如[0-9]表示匹配所有0-9的数字)
  5. 两个星号** 表示匹配任意中间目录(a/**/z可以匹配a/z、a/b/z或a/b/c/z等)

例子:

查看提交历史

回退到指定的版本

vuex

概述

是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享

一般情况下只有组件之间的数据共享才有必要存储到vuex中,对于组件中的私有数据,依旧存储在组件自身的data中

使用vuex统一管理状态的好处

  • 能够在vuex中集中管理共享的数据,易于开发和后期维护
  • 能够高效的实现组件之间的数据共享,提高开发效率
  • 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步

vuex基本使用

安装vuex依赖包

npm i vuex --save

导入vuex包

import Vuex from ‘Vuex’

Vue.use(Vuex)

创建store对象

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: {count: 0}

})

将store对象挂载到vue实例中

new Vue ({

​ el:‘#app’,

​ render: h=> h(app),

​ router,

//将创建的共享数据对象,挂载到vue实例中

//所有的组件,就可以从store中获取全局的数据了

​ store

})

vuex的核心概念

State

state提供唯一的公共数据源,所有共享的数据都要统一放到store的state中进行储存

//创建store数据源,提供唯一公共数据

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{},

​ actions:{}

})

组件访问State中数据的第一种方式:

this. s t o r e . s t a t e . 全 局 数 据 名 称 / / 例 如 t h i s . store.state.全局数据名称 //例如 this. store.state.//this.store.state.count

组件访问State中数据的第二种方式:

  1. 从vuex中按需导入mapState函数

import { mapState } from ‘vuex’

通过导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性:

  1. 将全局数据,映射为当前组件的计算属性

computed:{

…mapState([‘count’])

}

Mutations

Mutations用于变更Store中的数据

只有mutations中定义的函数,才有权利修改State中的数据

  • 只能通过mutations变更store数据,不可以直接操作store中的数据
  • 通过这种方式虽然操作稍微繁琐,但是可以集中监控所有数据的变化

this.$store.commit()是触发mutations的第一种方式

//定义 Mutations

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ add(state){

​ //变更状态

​ state.count++

}

},

​ actions:{}

})

//在组件中触发mutation

methods: {

handlel:{

​ //触发mutations的第一种方式

​ this.$store.commit(‘add’)

}

}

可以在触发mutations时传递参数

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ addN(state,step){

​ //变更状态

​ state.count += step

}

},

​ actions:{}

})

//在组件中触发mutation

methods: {

handlel2:{

​ //触发mutations的第一种方式

​ this.$store.commit(‘addN’,3)

}

}

第二种方式

  1. 从vuex中按需导入mapMutations 函数

import { mapMutations } from ‘vuex’

通过导入的mapMutations函数,映射为当前组件的methods函数

methods:{

​ …mapMutations([‘add’,‘addN’])

}

mutations中不能使用异步操作(定时器等),可以再action中使用

Actions

Actions用于处理异步任务

如果通过异步操作变更数据,必须通过Actions,而不能使用Mutations,但是在Actions中还是要通过触发Mutations方式间接变更数据

this.$store.dispatch()触发actions的第一种方式

//定义 Action

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ add(state){

​ //变更状态

​ state.count++

}

},

​ actions:{

​ addAsync(context){

​ setTimeout( () => {

​ //在 Actions中,不能直接修改state中的数据;

​ //必须通过context.commit()触发某个mutations才行

​ context commit(‘add’)

​ },1000)

}

}

})

//触发Actions

methods: {

​ handle(){

​ //触发actions的第一种方式

​ this.$store.dispatch(‘addAsync’)

}

}

触发actions异步任务时携带参数

//定义 Action

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ add(state,step){

​ //变更状态

​ state.count += step

}

},

​ actions:{

​ addAsync(context,step){

​ setTimeout( () => {

​ //在 Actions中,不能直接修改state中的数据;

​ //必须通过context.commit()触发某个mutations才行

​ context.commit(‘add’,step)

​ },1000)

}

}

})

//触发Actions

methods: {

​ handle(){

​ //触发actions的第一种方式

​ this.$store.dispatch(‘addAsync’,5)

}

}

第二种

  1. 从vuex中按需导入mapActions 函数

import { mapActions } from ‘vuex’

通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods函数

methods:{

​ …mapActions([‘addASync’,‘addNAsync’])

}

Getter

Getter用于对store中的数据进行加工处理形成新的数据

  1. Getter可以对store中的数据进行加工处理形成新的数据,类似于Vue的计算属性
  2. Store中数据发送变化,Getter的数据也会跟着变化

//定义 Getter

const store = new Vuex.Store({

​ state: {

​ count: 0

},

​ getters: {

​ showNum: state => {

​ return ‘当前最新的数量是【’+ state.count +‘】’

}

}

})

使用方式

this.$store .getters.名称

import { mapGetters } from ‘vuex’

computed:{

…mapGetters([‘showNum’])

}

Module

当遇见大型项目时,数据量大,store就会显得很臃肿

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
第二种方式

  1. 从vuex中按需导入mapMutations 函数

import { mapMutations } from ‘vuex’

通过导入的mapMutations函数,映射为当前组件的methods函数

methods:{

​ …mapMutations([‘add’,‘addN’])

}

mutations中不能使用异步操作(定时器等),可以再action中使用

Actions

Actions用于处理异步任务

如果通过异步操作变更数据,必须通过Actions,而不能使用Mutations,但是在Actions中还是要通过触发Mutations方式间接变更数据

this.$store.dispatch()触发actions的第一种方式

//定义 Action

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ add(state){

​ //变更状态

​ state.count++

}

},

​ actions:{

​ addAsync(context){

​ setTimeout( () => {

​ //在 Actions中,不能直接修改state中的数据;

​ //必须通过context.commit()触发某个mutations才行

​ context commit(‘add’)

​ },1000)

}

}

})

//触发Actions

methods: {

​ handle(){

​ //触发actions的第一种方式

​ this.$store.dispatch(‘addAsync’)

}

}

触发actions异步任务时携带参数

//定义 Action

const store = new Vuex.Store({

​ //state 中存放的就是全局共享的数据

​ state: { count: 0 },

​ mutations:{

​ add(state,step){

​ //变更状态

​ state.count += step

}

},

​ actions:{

​ addAsync(context,step){

​ setTimeout( () => {

​ //在 Actions中,不能直接修改state中的数据;

​ //必须通过context.commit()触发某个mutations才行

​ context.commit(‘add’,step)

​ },1000)

}

}

})

//触发Actions

methods: {

​ handle(){

​ //触发actions的第一种方式

​ this.$store.dispatch(‘addAsync’,5)

}

}

第二种

  1. 从vuex中按需导入mapActions 函数

import { mapActions } from ‘vuex’

通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods函数

methods:{

​ …mapActions([‘addASync’,‘addNAsync’])

}

Getter

Getter用于对store中的数据进行加工处理形成新的数据

  1. Getter可以对store中的数据进行加工处理形成新的数据,类似于Vue的计算属性
  2. Store中数据发送变化,Getter的数据也会跟着变化

//定义 Getter

const store = new Vuex.Store({

​ state: {

​ count: 0

},

​ getters: {

​ showNum: state => {

​ return ‘当前最新的数量是【’+ state.count +‘】’

}

}

})

使用方式

this.$store .getters.名称

import { mapGetters } from ‘vuex’

computed:{

…mapGetters([‘showNum’])

}

Module

当遇见大型项目时,数据量大,store就会显得很臃肿

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

你可能感兴趣的:(webpack,前端,javascript,vue.js)