基础甲骨文2

news/2024/10/25 16:22:20/

Promise构造函数:

多层回调函数的相互嵌套,就形成了回调地狱。牵一发而动全身,难以维护,可读性差。

为了解决回调地狱,ES6引入了Promise构造函数概念。

① Promise 是一个构造函数

我们可以创建 Promise 的实例 const p = new Promise()

new 出来的 Promise 实例对象,代表一个异步操作

② Promise.prototype 上包含一个 .then() 方法

每一次 new Promise() 构造函数得到的实例对象,

都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

③ .then() 方法用来预先指定成功和失败的回调函数

p.then(成功的回调函数,失败的回调函数)

p.then(result => { }, error => { }) 两个回调函数作为参数

调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

promise流程图.png

// promise读取文件
const fs = require("fs");   //node.js环境下// 封装读取文件方法
function read(fpath) {let p = new Promise(function (resolve, reject) {fs.readFile(fpath, "utf8", (err, dataStr) => {if (err) return reject(err);resolve(dataStr);});});return p;
}read("./files/11.txt").catch(function (err) {console.log(err.message);return 999}).then(function (data1) {console.log(data1);return read("./files/2.txt");}).then(function (data2) {console.log(data2);return read("./files/3.txt");}).then(function (data3) {console.log(data3);}).finally(function () {console.log("我执行了");});
------------------------------------------
ENOENT: no such file or directory, open 'E:\导航文件\资料文件\就业班课程内容\12 node资料\node-biji\07-promise\files\11.txt'
999
222
333
我执行了

.then 链式调用的优点: 解决了回调地狱的问题

.then 链式调用的缺点: 代码冗余、阅读性差、 不易理解

.catch 捕获错误

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

Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)注意:数组中 Promise 实例的顺序, 就是最终结果的顺序!

import thenFs from 'then-fs'
const promiseArr = [thenFs.readFile('./files/3.txt', 'utf8'),thenFs.readFile('./files/2.txt', 'utf8'),thenFs.readFile('./files/1.txt', 'utf8'),
]Promise.all(promiseArr).then(result => {console.log(result)
})
--------------------------------------------------
[ '333', '222', '111' ]

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

sync-await解决异步嵌套终极方案.png

async-await.png

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

async-await是解决异步嵌套 终极方案。(让异步代码按特定顺序执行)

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

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

async.png

EventLoop:

为了防止耗时任务(异步任务)导致程序假死的情况—> eventloop机制

同步/异步 JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这 个过程是循环不断的,所以整个的这种运行机 制又称为 EventLoop(事件循环)

//javascript是一门运行在“客户端”,面向对象的,事件驱动的,单线程的编程语言//代码的执行机制://1.主线程的代码全部执行完毕时才会执行异步代码//2.异步代码://      setTimeout   setInterval          =>时间到了   //      以on开头的事件                      =>事件触发了//      ajax的回调函数 onreadystatechange   =>数据从服务器返回了//前端异步的代码//1.setTimeout   setInterval//2.以on开头的事件  如:btn.onclick     onchange(输入框懒加载)    onblur失去焦点//3.ajax的回调函数 onreadystatechange 状态值改变
为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:
① 同步任务(synchronous)又叫做非耗时任务,指的是在主线程上排队执行的那些任务,顺序执行只有前一个任务执行完毕,才能执行后一个任务
② 异步任务(asynchronous)又叫做耗时任务,异步任务由 JavaScript 委托给 宿主环境(浏览器或者node等)进行执行当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
① 宏任务(macrotask)异步 Ajax 请求、setTimeout、setInterval、文件操作其它宏任务...
② 微任务(microtask)Promise.then /.catch / .finallyprocess.nextTick其它微任务先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 先微后宏 

同步任务异步任务.png

宏任务与微任务.png

事件队列,先微后宏.png

webpack:

概念:

webpack本身是, node的一个第三方模块包, 用于静态模块打包(module bundler)。

webpack默认只能处理js类型文件和 json文件,处理css需要 style-loader + css-loader 两个依赖包。

  1. 减少文件数量,压缩代码体积,提高加载速度
  2. 支持less/sass => css
  3. 支持ES6/7/8 => ES5 兼容性优雅降级

模块化开发规范(CommonJS / ES6):

node.js --> commonJS规范:

// nodejs - commonJS规范-规定了导出和导入方式// 导出 module.exports = {}
// 导入 const 变量 = require("模块标识")

ES6规范:

// 导出 export 或者 export default {}
// 导入 import 变量名 from '模块标识'

package.json中的dependencies和 devDependencies区别和作用:

  • dependencies 别人使用你的包必须下载的依赖, 比如yarn add jquery
  • devDependencies 开发你的包需要依赖的包, 比如yarn add webpack webpack-cli -D (-D 相当于 --save-dev) 开发依赖包

import语法浏览器支持性不好, 需要被webpack转换后, 再使用JS代码。

图片处理.png

webpack图片打包.png

js高级语法兼容性降级处理.png

webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全(必会):

1. 初始化参数:从配置文件读取与合并参数,得出最终的参数
2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,开始执行编译
3. 确定入口:根据配置中的 entry 找出所有的入口文件
4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
5. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

npm下载—镜像源

npm config get registry 查看当前的下包镜像源

npm config set registry=https://registry.npmmirror.com/ 下载最新淘宝镜像源

nrm ls 查看所有可用的镜像源

vue:

创建一个新的vue项目:

1,vue create 文件名 //创建一个新的脚手架项目

​ 注意: 项目名不能带大写字母, 中文和特殊符号

2,cd 文件名 //切换到新建的脚手架项目文件夹

3,npm run serve //运行vue项目

4,将vue.config.js文件创建到src文件夹并列位置 //关闭错误检查

module.exports = {devServer:{  //自定义服务器配置port: 3000,  //端口open: true}lintOnSave: false  //关闭eslint检查
}

什么是对象上的, 属性和方法:

let obj = { // 属性指的是a, b, c, d, e这些名字a: 10,b: [1, 2, 3],c: function(){},d () {},e: () => {} // 值是冒号:右边的值
}
this指向口诀

在function函数中, this默认指向当前函数的调用者 调用者.函数名()

在箭头函数中, this指向外层"函数"作用域this的值

@vue/cli 目录和代码分析

node_modules下都是下载的第三方包
public/index.html – 浏览器运行的网页
src/main.js – webpack打包的入口文件
src/App.vue – vue项目入口页面
package.json – 依赖包列表文件

main.js.png

vue_main_index流程图.png

关闭检查规则:

1,关闭所有规则:vue.config.js–>module.exports:{lintOnSave:false}

2,关闭某一个错误检查规则:package.json–>“eslintConfig”:{“rules”:{“no-unused-vars[错误代码]”:"off}}

插值表达式:

又叫(声明式渲染/文本插值)

{{ obj.age > 18 ? " 成年 " : " 未成年 " }} 里面放三元表达式

MVVM设计模式:

设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

mvvm.png

vue指令:

v-bind 把vue数据变量的值, 赋予给 Dom原生属性 上, 影响标签显示效果
<template><div><!-- vue指令 v-bind属性动态赋值 --><!-- v-bind:属性名="变量名" --><!-- 简写  :属性名="变量名" --><a v-bind:href="url">我是a标签</a><img :src="localImg"></div>
</template><script>
import imgObj from './assets/1.gif' 
// 唯独js需要导入本地文件时,不能直接引用地址,必须先 import,因为webpack默认不会对动态路径主动打包。
export default {data(){return{url: 'http://www.baidu.com',localImg:imgObj}}
}
</script>

动态变量图片路径import.png

v-on 给标签绑定事件:
  • 语法
    • v-on:事件名=“要执行的少量代码
    • v-on:事件名=“methods中的函数”
    • v-on:事件名=“methods中的函数(实参)”
    • v-on:事件名=“methods中的函数(实参,$event)” —> 获取v-on事件处理函数的对象e
  • 简写: v-on ----> @
  • 注意:methods中的函数调用data里的变量需要添加 this
  • @事件名.修饰符=“methods里函数”
    • .stop - 阻止事件冒泡
    • .prevent - 阻止默认行为
    • .once - 程序运行期间, 只触发一次事件处理函数,事件一直可以触发
    • @keyup.enter - 监测回车按键
    • @keyup.esc - 监测返回按键…
<a @click="one" .......
<a @click="two(10, $event)" .......  // $event固定写法
<a @click.prevent="fn"
...
one(e){e.preventDefault()},
two(num, e){e.preventDefault()}
字符串转数组再转字符串
this.Str.split("").reverse().join("") === [...this.Str].reverse( ).join("")         // 字符串的扩展运算符

🆘重点:v-model 把 <表单标签> 的value属性 和vue数据变量 , 双向绑定到一起

数据双向绑定原理:
<template>
<input v-bind:value="username" @input/change="username = $event.target.value">//input热加载 --- change懒加载(值改变,失去光标)
</template>
<script>
export default {data() {return {username: 'luojiajie'}}
}
</script>

v-model的使用.png

// 特别注意: v-model, 在input[checkbox]的多选框状态
// 变量 为 非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
// 变量 为 数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值

表单输入的value值是字符串型,所以:

v-model.修饰符=“vue数据变量”

  • .number 以parseFloat转成数字类型
  • .trim 去除首尾空白字符
  • .lazy 在onchange(失去焦点并且内容改变)时 触发而非input时
v-text和v-html:

更新DOM对象的innerText/innerHTML

语法:

  • v-text=“数据变量”
  • v-html=“数据变量”

注意: 会覆盖插值表达式

v-text把值当成普通字符串显示, v-html把值当做html解析

v-show和v-if
  • 语法:
    • v-show=“vue变量/boolean”
    • v-if=“vue变量/boolean”
  • 原理
    • v-show 用的 display:none 隐藏 (频繁切换使用,效率更高)
    • v-if 直接从DOM树上移除—>v-for一般不与v-if搭配使用
  • 高级
    • v-else-if使用 v-else
v-for

列表渲染, 所在标签结构, 按照数据数量, 循环生成

  • 语法

    • v-for=“(值ele, 索引 i ) in 目标结构”
    • v-for=“值 in 目标结构”
  • 目标结构:

    • 可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)
  • 注意:

    v-for的临时变量名不能用到v-for范围外

  • 更新监测:data里数组变更—>页面更新 🆘异步的🆘

    • 数组变更方法, 就会导致v-for更新, 页面更新

      数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用新数组覆盖旧数组 或 **this.$set()**方法

      $set的用法:

      this.$set(this.arr数组,索引index,新值val)   
      //更新 数组 索引为index的值为 新值valthis.$set(this.arr[index], "目标对象属性名", 新值val);
      //目标 元素对象 ,目标对象属性名,新值valthis.arr.splice(1, 0, "新来的")   //索引为1的位置删除0个元素。新增‘新来的’元素
      let newarr = arr.slice(1,2)  
      //索引为1的位置截取到索引为2的元素,不包含索引为2,返回值为新数组 ,注意第二个参数是索引号拓展:
      arr.findIndex(obj=>obj.id===变量) //查找需要的对象元素的 索引
      arr.find(obj=>obj.id===变量) //也是迭代方法,返回符合条件的 数组元素对象
      arr.indexOf(元素值)--->//查找对应值的 索引号,未查到到返回-1
      
回流(重排): 页面元素属性发生变化,导致位置,大小等影响到其他元素时发生回流。当浏览器必须重新处理和绘制部分或全部页面时,回流就会发生
重绘: 不影响布局, 如颜色,字体系列等发生变化,只是标签页面发生变化, 重新绘制
注意: 回流(重排)必引发重绘, 重绘不一定引发回流(重排)
push()在数组末尾添加
pop()在数组末尾删除shift()在数组开头删除
unshift()在数组开头增加splice
reverse
sort((a,b)=>a-b)  //接受一个比较函数作为参数,比较函数的参数为数组元素(对象),return三种情况-1,1,0
// function compare(value1,vakue2){if(value1.val<value2.val){return -1;} else if(value1.val>value2.val){return 1;} else {return 0}
}
arr.sort(compare);

构造函数new关键字创建一个实例对象执行4步骤:创建空对象并让this指向该空对象.

深入响应式原理:

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property属性,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter,它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。每个组件实例都对应一个 watcher 侦听器/监听者 实例,它会在组件渲染成虚拟DOM的过程中把“接触”过的数据记录为依赖。之后当依赖项(数据层Model发生变化,数据变更时)的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。当 getter 触发(视图层View发生变化时)时,会通知 watcher,从而使视图层变化影响到数据层的变化。

vue.js采用数据劫持<—>结合发布-订阅者模式,通过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变动时,发布消息给订阅者,触发响应的监听回调。

响应式原理.png

响应式检测变化:

对于对象,Vue 无法检测 property (属性)的新增或移除

对于数组:Vue 不能检测以下数组的变动:

  1. 当你利用索引直接更新一个数组项时,例如:arr[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:arr.length = newLength
<script>
var person = {age: 0
}
person._age = person.age
Object.defineProperty(person, 'age', {set(val){console.log('触发了set方法', val)if(val>0 && val <=100){this._age = val}},get(){console.log('触发了get方法')return `年龄${this._age}`}
})// person.age //触发了get方法
person.age = 20
</script>

JS对象中的get和set方法:

🧨重点🧨取值器get 赋值器set

var person = {_age: 20,set age(val){console.log('set方法',val)if(val>0 && val <=100){this._age = val}},get age(){console.log('get方法')return `年龄${this._age}`}
}
person.age //get方法
person.age = 20

v-for的就地更新–虚拟Dom–diff算法:🧨重点🧨

目的:提高数据更新的性能。

App.vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 存于内存中,才会渲染显示到真实DOM页面上。

(虚拟DOM本质是个JS对象)因为真实的DOM属性好几百个,虚拟DOM只包含必要的属性

当数据发生改变的时候,vue机制会在内存中生成新的虚拟DOM结构,和旧的虚拟DOM做对比,利用diff算法,找到不同,只更新变化的部分(重绘/回流)到页面 - 也叫打补丁。

diff算法:

情况1: 根元素变了, 删除旧虚拟DOM重建

情况2: 根元素没变, 属性改变, 旧虚拟DOM元素复用, 就地更新属性

情况3: 根元素未变, 属性未变, 子元素/内容改变

​ 3.1 无key – 就地更新;

​ 3.2 有key – 按key比较 :

  • ​ 3.2.1 key值为索引 -->还是就地更新,key存在就复用此标签更新内容, 如果不存在就直接建立一个新的

  • ​ 3.2.2 key值为ID (key的值只能是唯一不重复的, 字符串或数值) --> 得到一个排序提示 v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示。

diff算法对比动图.gif

动态class:

:class=“变量名称”;---->变量为类名

:class=“{类名:布尔值变量}” ---->变量为 true/false

:class=
"{rever类名: arr.includes(item), hide类名:!arr.includes(item)&&arr.length===3}"
// 多类名动态切换 arr.includes(item)判断数组中书否包含指定元素

动态style:

:style=“{ backgroundColor: ‘red’ }” —>'red’字符串

:style=“{ backgroundColor: colorStr }” —>colorStr 变量名

:style=“{ ‘background-color’: colorStr }”

vue过滤器:

过滤器只能用在, 插值表达式v-bind表达式

  • 全局定义 Vue.filter(“过滤器名”, (值) => {return “返回处理后的值”})
  • 局部定义 filters: {过滤器名字: (值) => {return “返回处理后的值”}

定义全局过滤器:main.js里面定义,所有 . vue文件都能直接使用

Vue.filter(“reverse”, val => val.split(“”).reverse().join(“”))

定义局部过滤器:xxx.vue组件里面定义,只限当前 . vue文件使用

filters: {            // 注意局部过滤器 filter's' 写在data并列位置     必须要有返回值filter11 (val) {return  moment(val).format("YYYY-MM-DD")},filter2 : (val,形参1,形参2...) => moment(val).format("YYYY-MM-DD"),  //过滤器名:箭头函数}
------------------------------------------------------
// 全局过滤器接参数
Vue.filter("reverse", (val, s) => {return val.split("").reverse().join(s)
})
使用过滤器:  {{ msg | reverse('|') }}  /  :title="msg | toUp | reverse('|')"

vue计算属性-computed:

必须写 return

computed: {"计算变量" () {   // 计算属性: 一个变量的值, 需要用另外变量计算而得来return "值"	 // 函数内变量变化, 会自动重新计算结果返回}
}
-----------------------------------------------------
computed: {          // 完整写法:当需要给计算属性变量赋值的时候"计算变量": {set(计算变量的新值val){ // 修改计算属性触发set方法},get() {     //  使用获取计算属性触发get方法return "值"}}
}
vue计算属性—缓存功能:

计算属性根据依赖变量的结果缓存, 依赖变化–>重新计算结果存入缓存, 依赖未发生变化–>直接使用缓存值不再调用函数,比普通方法性能更高

vue计算属性-缓存.png

reduce的使用:

let arr = [{a:1,b:18},{a:2,b:10},{a:3,b:15},{a:4,b:20}]
// reduce接受两个参数
arr.reduce((sum, val) => sum += val.b,0)

vue侦听器-watch:

可以侦听 data / computed 属性值改变,当侦听的属性的属性值改变时,自动触发处理函数。

watch: {"要侦听的属性名": {immediate: true, // 立即执行(网页打开handler执行一次)deep: true, // 深度侦听复杂数据类型 属性值内 的变化handler (newVal, oldVal) {  }}
}

vue组件:

一个自定义的标签,用来封装一个特定功能的可复用的 Vue 实例,包含结构样式行为,最大化的复用代码。

全局注册vue.component---------局部注册components

局部注册.png

<style scoped>  css属性只在当前组件中生效,为**当前组件**中的元素添加一个自定义属性名data-v-xxxxxxxx ,避免样式污染网页其他组件,组件里面的组件样式修改需要/deep/穿透 

🆘🆘🆘每个组件的变量和值都是独立的---->组件通信:

1,父子通信传值 :$emit / @自定义事件名,,
2,无关系组件之间通信传值Vuex / eventBus,,
3,ref获取子组件也可以通信传值(this.$refs.子组件ref名.子组件数据变量名),,
4,this.$parent.父组件数据变量名(不推荐,维护困难,难以找到是谁改变了父组件的数据),,
5,this.$childen.子组件数据变量名,,
6,v-model传值(父组件传值给子组件,子组件使用并通知父组件修改),,.sync语法糖也可以
7,provide / inject 祖先组件向后代传值,,
8 , 路由跳转传参

在vue中需要遵循单向数据流原则 -->数据从父组件流向子组件

1. 父组件的数据发生了改变,子组件会**自动**跟着变
2. 子组件不能直接修改父组件传递过来的props数据  props是**只读**

Vue规定props==里的变量, ==本身是只读的,props的值不能在子组件重新赋值。

父子之间传值.jpg

EvenBus: 兄弟组件之间传值

兄弟组件之间传值.png

创建一个第三方**js文件(**导入vue对象,并导出一个vue实例),相互传值的组件分别引入这个js文件,传出用eventBus. e m i t ,接收用 e v e n t B u s . emit,接收用eventBus. emit,接收用eventBus.on

解读:‘send’ ----->自定义事件名///

给子组件List 传值传的是一个复杂数据类型 ‘arr’ ,所以在子组件内部修改数据会使父组件数据改变,故未再子传父通信(eslint会报错)。

eventBus使用较少,因为子组件接收到需要更新数据过后,在该子组件直接修改数据又是Vue所不建议的,所以有需要传值给父组件。

Todos案例切换栏目显示:

tab栏目切换方法一:子组件多个点击事件处理函数 触发同一个主组件事件

todu切换栏目显示.png

tab栏目切换方法二:子组件事件代理 多个点击事件,然后触发主组件事件处理函数(全选)

tab栏目切换方法二.png

Todos案例全选:

todo全选-小选框.png

正规做法:todo全选.png

钩子函数:

生命周期.jpg

解读:

编译模板阶段 –开始分析

1.Has el option? – 是否有el选项 – 检查要挂到哪里

​ 没有. 调用$mount()手动指定挂载点

​ 有, 继续检查template选项

2.template选项检查

​ 有 - 编译template到render渲染函数中

​ 无 – 编译el选项对应标签作为template(要渲染的模板)

axios:

特点

  • 支持客户端发送Ajax请求
  • 支持服务端Node.js发送请求
  • 支持Promise相关用法
  • 支持请求和响应的拦截器功能
  • 自动转换JSON数据
  • axios 底层还是原生js实现, 内部通过Promise封装的

扩展–对象合并

    let a = {username: 'zs'}let b = {age: 20}//es5当中let o = {username: a.username,age: b.age}// es6当中方法1// 通过扩展元素符let o = {...a,...b}// 方法2 通过Object.assign  浅拷贝let o = {}Object.assign(o, a, b) console.log(o)

axios基本使用:

axios({method: '请求方式', // get posturl: '请求地址',data: {    // 拼接到请求体的参数,  post请求的参数xxx: xxx,},params: {  // 拼接到请求行的参数, get请求的参数  -->get没有请求体xxx: xxx }
}).then(res => {    // axios()原地返回一个Promise对象console.log(res.data) // 后台返回的结果
}).catch(err => {console.log(err) // 后台报错返回
})
-----------------------------------------
axios.defaults.baseURL = "http://123.57.109.30:3006"    // 添加全局基地址

扩展-Promise:

    let p = new Promise((resolve, reject) => {setTimeout(() => {//resolve成功回调函数// resolve('123')//reject失败回调函数reject({ status: 500, message: '失败' })}, 1000)})p.then((data) => {console.log(data) ----> .then里面只写一个回调函数时表示成功的回调函数resolve}).catch(error => {   ----> 若无catch捕捉错误信息,且.then里面只写一个回调函数时,无打印结果console.log(error)})

使用$refs(获取Dom/)获取子组件(就是一个自定义标签),实现组件通信的方法:

原生获取(不建议)或者 ref = ‘自定义name’ --> $refs.自定义name

_refs实现组件通信.png

Vue更新数据是一个异步操作(异步微任务),解决异步操作顺序执行的四种方法:

_refs的使用.png

解决异步问题的方法.png

Vue 的 $nextTick 的原理是什么?

为什么需要 nextTick ,Vue 是异步修改 DOM 的(异步微任务)并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改–刷新后的 DOM 做相应的处理,这时候就可以使用 Vue.nextTick(callback)这个 api 了。nextTick 的原理正是 vue 通过异步队列控制 DOM 更新和 nextTick 回调函数先后执行的方式。

VM.$nextTick 是一个微任务 (先微后宏)

第一次加载页面会触发哪几个钩子函数?

当页面第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子函数。

props: 定义校验方式

  props: {background: String,color: {type: String,default: "#fff", //指定默认值},title: {type: String,required: true, //指定必填项},},-----------------props: ['title','list']

动态组件:

vue内置组件设置挂载点, 配合is属性, 设置要显示的组件名字 效果和v-if / v-else一样。

子路由出口 --> 等同于对动态组件的进一步封装。

<template>        // 切换 登录/注册页面<div><button @click="comName = 'UserName'">账号密码填写</button><button @click="comName = 'UserInfo'">个人信息填写</button><p>下面显示注册组件-动态切换:</p>  <div style="border: 1px solid red;"><keep-alive><component :is="comName"></component>//挂载点<component>  :is="组件变量"  动态创建和销毁//挂载点<component>  :is="'组件名'"  直接使用组件名时,里面为字符串🆘🆘🆘</keep-alive>  //缓存,激活/非激活</div></div>
</template>    
组件切换会导致组件被频繁销毁和重新创建, 性能不高 --> 组件缓存:

Vue内置的 组件 包起来要频繁切换的组件 或者 子路由出口 -->让动态组件缓存,不再创建和销毁,而是激活和非激活。

补充2个钩子方法名:

​ activated – 激活时触发

​ deactivated – 失去激活状态触发

组件插槽:

用于实现组件的内容分发,通过 slot 标签占位,可以接收到写在组件标签内的内容

  1. 组件内用默认内容占位
  2. 使用组件时夹着的地方, 传入标签替换slot

具名插槽:

//子组件不确定的地方  slot占位
<slot name="title"></slot>
--------------------------------------
//主组件:
<Pannel><template v-slot:title>     // v-slot:组件名 可以简化成 #组件名 使用  //具名插槽必须在主组件中添加名字<h4>芙蓉楼送辛渐</h4></template>
</Pannel>
// slot的name属性起插槽名, 使用组件时, <template>配合#插槽名传入具体标签

🆘🆘作用域插槽:

在给插槽赋值时想在父组件环境下使用子组件里的变量

1,子组件, 在slot上绑定属性和子组件内的值 传值给父组件   :变量名1 = '子组件变量名'
2,父组件中,<template>配合v-slot="变量名2"2.6.0之后) 注意是 '='  接收参数
3,父组件标签内使用子组件中的变量。   变量名2.变量名1--->'子组件变量名'

插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。

作用域插槽.png

row ---->自定义变量名

具名插槽和作用域插槽一起使用:

具名插槽和作用域插槽一起使用.png

自定义指令:

指令名 需要加 ” “ 引号

binding —> 一个对象,binding.value就是使用指令时传入的参数。例如:v-color=" ‘red’ "中的 ’red‘

扩展:开发依赖 / 生产(使用)依赖:

开发依赖-生产依赖.png

对象解构:

路由:

vue里面 路径和组件的映射关系—>使用场景:在一个页面中切换业务场景

网易云音乐—>单页面应用(SPA): 所有功能在一个html页面上实现

—>前端路由优缺点:整体不刷新页面,数据传递简单,体验好效率高;首次加载比较慢,不利于SEO搜索

@/ —> src绝对路径

使用步骤

1,安装:
npm i vue-router
2,在main.js中导入路由:
import VueRouter from 'vue-router'
3,使用路由插件:
Vue.use(VueRouter)
4,创建路由规则数组:
const routes = [{path: "/find",component: Find},{path: "/my",component: My},...
]
5, 创建路由对象 - 传入规则:
const router = new VueRouter({routes
})
6, 关联到vue实例:
new Vue({router
})
7, App.vue中设置挂载点:
<--当url的hash值路径切换,显示规则里对应的组件到这里-->
<router-view></router-view>

vue路由 - 声明式导航:

可用全局组件router-link来替代a标签,to 来代替 href:

<a href="#/find">a链接</a>
<router-link to="/find">发现音乐</router-link> 
//声明式导航高亮的功能(自带类名) .router-link-active激活时自动添加类名<span @click="btn('/my' 或者 'My')">我的音乐</span>  // js编程式导航,name

声明式导航 - 跳转传参:

传参:跳转路径上拼接 查询字符串 ?key=value   子组件页面用 this.$route.query.key 取值传参:跳转路径上拼接 /值    (提前在路由规则/path/:key)  子组件页面用 this.$route.params.key 取值

查询字符串传参-动态参数传参.png

vue路由 - 重定向:

例如: 网页默认打开, 匹配路由"/“, 强制切换到”/find"上

const routes = [{path: "/", // 默认hash值路径redirect: "/find" // 重定向到/find 路径上,重新匹配当前规则数组// 浏览器url中#后的路径被改变成/find-重新匹配数组规则},......// 404 找不到路径:// 1.创建NotFound组件页面 2.引入到main.js 3.在规则数组最后(规则是从前往后逐个比较path){path: "*",component: NotFound }
]

模式设置:

目标: 修改路由在地址栏的模式

hash路由例如: http://localhost:8080/#/home

history路由例如: http://localhost:8080/home (以后上线需要服务器端支持, 否则找的是文件夹)

模式文档

main.js

const router = new VueRouter({routes,mode: "history" // 打包上线后需要后台支持, 默认模式是hash
})

vue路由 - 编程式导航:

<template><div><div class="footer_wrap"><span @click="btn('/find'或者'Find')">发现音乐</span> //name在router/index.js定义<span @click="oneBtn">朋友-小传</span>               //或者main.js路由中定义</div><div class="top"><router-view></router-view></div></div>
</template>
<script>
// 目标: 编程式导航 - 跳转路由传参
// 方式1:
// params => $route.params.参数名  接收
// 方式2:
// query => $route.query.参数名  接收
// 重要: path会自动忽略params
// 推荐: name+query方式传参
// 注意: 如果当前url上"hash值和?参数"与你要跳转到的"hash值和?参数"一致, 爆出冗余导航的问题, 不会跳转路由//当前页面点击跳转当前页面,会触发警告冗余导航的问题
export default {methods: {btn(targetPath 或者 targetName){this.$router.push({// path: targetPath,name: targetName    // 虽然用name跳转, 但是url的hash值还是切换path路径值}) 					// 方便修改: name路由名(在页面上看不见随便定义)},						// path可以在url的hash值看到(尽量符合组内规范)oneBtn(){this.$router.push({name: 'Part',//path: '/Part'params: {          // path会自动忽略paramsusername: '小传'}//query: {//  username: '小传'// }})}}
};
</script>

传递是this.$router 路由实例对象 上面添加跳转新的路由

接收$route. 具体某个路由对象里面接收参数

js编程式导航与传参.png

编程式传参.png

vue路由 - 路由嵌套:

main.js– 配置2级路由

一级路由path从/开始定义

二级路由往后path直接写名字, 无需/开头

嵌套路由在上级路由的children数组里编写路由信息对象

const routes = [// ...省略其他{path: "/find",      //--->注意 /name: "Find",component: Find,children: [         //--->二级路由{path: "recommend",      //--->注意 直接写路径  无 /component: Recommend},{path: "ranking",component: Ranking}]}// ...省略其他
]

声明式导航类名区别:

声明式导航类名区别.png

路由跳转传参总结

跳转方法传参位置路由规则接收
/path?key=value无特殊$route.query.key
/path/值/path/:key$route.params.key
this.$router.push({path: “/path”, query: {key: value}})query的对象无特殊$route.query.key
this.$router.push({name: “com”, params: {key: value})params的对象路由规则需要name属性$route.params.key(注意,这种在内存中保存)

1、this.$router.push()跳转到指定的url,并在history中添加记录,点击回退返回到上一个页面

2、this.$router.replace()跳转到指定的url,但是history中不会添加记录,点击回退到上上个页面

3、this.$touter.go(n)向前或者后跳转n个页面,n可以是正数也可以是负数

4、格外注意: 使用path会自动忽略params

全局前置守卫:

// 目标: 路由守卫
// 场景: 当你要对路由权限判断时
// 语法: router.beforeEach((to, from, next)=>{//路由跳转"之前"先执行这里, 决定是否跳转})
// 参数1: 要跳转到的路由 (路由对象信息)    目标
// 参数2: 从哪里跳转的路由 (路由对象信息)  来源
// 参数3: 函数体 - next()才会让路由正常的跳转切换, next(false)在原地停留, next("强制修改到另一个路由路径上")
// 注意: 如果不调用next, 页面留在原地// 例子: 判断用户是否登录, 是否决定去"我的音乐"/my
const isLogin = true; // 登录状态(未登录)
router.beforeEach((to, from, next) => {if (to.path === "/my" && isLogin === false) {alert("请登录")next(false) // 阻止路由跳转} else {next() // 正常放行}
})

Vant 组件库:

1,全局自动按需引入vant组件:(推荐方式)

plugins: [          // babel.config.js 配置文件["import",{libraryName: "vant",libraryDirectory: "es",style: true,},"vant",],],

vant使用推荐.png

2,手动按需: 3,全局全部引入组件:(,体积稍大,不推荐)

vant使用方法.png

节流和防抖:

// 节流: (减缓事件执行速度,坦克大战发射子弹)let timer = null;document.querySelector(".jl").addEventListener("click", () => {if (!timer) {    // 定时器为空,则进入定时器timer = setTimeout(() => {console.log("打印了一次");timer = null;   // 时间到了再将定时器timer清空,清空之前无法再次进入定时器// 这儿必须要用timer=null,,,clearTimeout(timer)不能清除自身}, 1000);}});// 防抖:(多次事件触发只执行最后一次动作,英雄回城)document.querySelector(".fd").addEventListener("click", () => {clearTimeout(timer);     // 进入定时器之前先清空所有定时器timer = setTimeout(() => {console.log("打印了一次");}, 1000);       // 每次触发函数体执行之前都清空timer,保证只有最后一次生效});

封装axios导出导入.png

Vant 动态设置 REM 基准值:

rem相对于html根字体font-size–>通过媒体查询(参考bootstrap)@media–>lib-flexible 自动设置HTML根标签字体大小(默认屏幕划分10等份)

npm i amfe-flexible / flexible

开发依赖包 -->postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位

npm i postcss-pxtorem@5.1.1 -D 注意版本,该插件不能转换行内样式中的 px

http和https:

  • https 协议:安全,对请求报文响应报文做加密

网络传输的安全性;对称加密和非对称加密;公钥和私钥

对称加密:

​ 加解密使用 相同 秘钥 高效,适用于大量数据的加密场景 算法公开,安全性取决于秘钥大小,但秘钥越大效率越低,需要权衡在安全和效率中做权衡。缺点:算法本身安全,但使用场景不够安全(秘钥传送安全问题),因为解密和加密都是同一个秘钥。

非对称加密:

使用 匹配的一对密钥分别进行加密和解密。公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。

  • 注意:公钥加密的数据 只能 用 对应的私钥解密,同理,私钥加密的数据 只能用 对应的公钥解密。公钥其实是根据私钥生成的
  • 用法概要:
    • 加密:对数据做加密
    • 签名:证明数据是谁发的

安全性高,但加解密复杂,效率低,耗时长。

公钥加密:

​ 用来针对互联网上加密数据传递,

补充:如果要在网络上相互发送密文,可以让对方也发对方的公钥过来,用对方的公钥来加密。

私钥签名:

​ 目的是为了将明文公布给别人,同时证明是自己发的;可以防止明文在传送给别人时被篡改。

第一步: James 用 James的私钥 对明文(我同意)的hash值进行加密,把加密后的密文(签名)和明文(我同意)一起发给 Linda。

第二步: Linda 用 James的公钥解密密文(签名),解密后的明文hash值 和 接收到的明文(我同意)的hash值进行对比,如果一样则是 James 发的。

https协议:

​ 非对称加密 -> 公钥传送安全问题–>使用 权威认证机构(CA) 来证明 网站的公钥没被篡改 -->https

使用流程:
  • 客户端 操作系统内置 权威认证机构(CA) 的机构证书X
  • 服务器A 找认证机构生成认证证书A(服务器的域名,证书有效期,证书颁发机构,服务器自己的公钥A等),并保存在服务器A中
  • 客户端浏览器 请求获取 服务器A证书
  • 客户端浏览器 用 机构证书X 解密 服务器A证书
    • 解密成功:获取 服务器的公钥A(只要解密成功,就说明 是 机构认证的)
    • 解密失败:认证失败
  • 浏览器 将自己的秘钥 发给服务器
    • 使用对称加密算法 生成 会话密钥B
    • 使用服务器A公钥会话密钥B做加密,并发给 服务器
  • 浏览器和服务器 使用会话秘钥B来对 请求报文 和 响应报文 做加密
function fn() {console.log (setTimeout(() => {console.log(456);}, 2000))}fn()   // 1   456  定时器id

vuex:

vuex是采用集中式管理组件依赖的共享数据的一个工具,可以解决不同组件数据共享问题。

vuex.png

结论

vuex存储数据时存储在内存中,刷新/F5会清除内存,从而导致vuex数据丢失。常见的保存token最保险的方式时在vuex和本地存储都存一下。

  1. 修改state状态/数据必须通过**mutations**
  2. mutations只能执行同步代码,类似ajax,定时器之类的代码不能在mutations中执行
  3. 执行异步代码/同步代码,要通过actions,然后将数据提交给mutations才可以完成。actions函数返回值为promise对象,一般await修饰。
  4. state的状态即共享数据可以在组件中引用
  5. 组件中可以调用action,用dispatch
 npm i vuex -S  // 运行依赖
在main.js中 import Vuex from 'vuex'Vue.use(Vuex)  // 调用了 vuex中的 一个install方法,安装注册const store = new Vuex.Store({...配置项}) /配置state mutations actions..store挂载在Vue实例上
state:  // 🆘🆘组件计算属性computed里面使用
定义state:const store  = new Vuex.Store({state: {count: 0,list: [1,5,6,8,7,9]}, // ... mutations  actions   getters
})组件中获取公共数据state: 方法1原始导入:this.$store.state.变量名  直接使用或者把state中数据,定义在组件内的计算属性中 再使用count变量computed: {count () {return this.$store.state.count}}方法2辅助函数:import { mapState } from 'vuex'  computed: {               ...mapState(['count'])}  
mutations:  // 🆘🆘组件方法methods里面使用
state数据的修改只能通过mutations:(mutations是一个对象,对象中存放修改state的方法)1,定义: mutations: {// 方法里参数 第一个参数是当前store的state属性// payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷addCount (state,payload) {state.变量 += payload}}, 2,组件中调用 原始方法:methods: {          //   调用方法addCount () {// 调用store中的mutations 提交给muations// commit('muations名称', 2)this.$store.commit('addCount', 10)  // 直接调用mutations里面的方法addCount}}3,辅助函数方法mapMutations:import  { mapMutations } from 'vuex'methods: {...mapMutations(['addCount']) === addCount(){this.$store.commit('addCount')}}actions:   // 🆘🆘组件方法methods里面使用1,定义actionsactions: {//  获取异步的数据 context表示当前的store的实例 可以通过 context.state 获取状态/数据 也可以通过context.commit 来提交/触发mutations, 也可以 context.diapatch调用其他的actiongetAsyncCount (context,payload) {setTimeout(function(){// 一秒钟之后 要给一个数 去修改statecontext.commit('addCount', payload)}, 1000)}} 2,原始传参调用:addAsyncCount () {this.$store.dispatch('getAsyncCount', 123)}3,辅助函数 mapActions 调用:import { mapActions } from 'vuex'
methods: {...mapActions(['getAsyncCount'])
}
<button @click="getAsyncCount(payload)">+异步</button>getters:  // 🆘🆘组件计算属性computed里面使用
1,定义getters:  --->相当于 vuex 中的计算属性getters: {// getters函数的 第一个参数 是 state // 必须要有 返回值 returnfilterList:  state =>  state.list.filter(item => item > 5)}
2,原始方法调用:<div>{{ $store.getters.filterList }}</div>
3,辅助函数 mapGetters:import { mapGetters } from 'vuex'computed: {                         ...mapGetters(['filterList'])}

vuex的模块化:

const store  = new Vuex.Store({modules: {user: {   //模块一:usernamespaced: true,state: {token: '12345'}mutations: {//  这里的state表示的是user的stateupdateToken (state) {state.token = 678910}}},setting: {   //模块二:settingstate: {name: 'Vuex实例'}}})   
//请注意: 此时要获取子模块的状态数据 需要通过 $store.state.模块名称.属性名 来获取

模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

user模块还是setting模块,它的 action、mutation 和 getter 其实并没有区分,都可以直接通过全局的方式调用。

但是,如果我们想保证内部模块的高封闭性,我们可以采用namespaced命名空间 来进行设置。

使用带命名空间的模块 actions/mutations

方案1:直接调用-带上模块的属性名路径

test () {this.$store.dispatch('user/updateToken') // 直接调用方法
}

方案2:辅助函数-带上模块的 “属性名/方法名”

  methods: {...mapMutations(['user/updateToken']),test () {this['user/updateToken']()}}<button @click="test">修改token</button>

方案3: createNamespacedHelpers 创建基于某个命名空间辅助函数

import { mapGetters, createNamespacedHelpers } from 'vuex'
const { mapMutations } = createNamespacedHelpers('user')
<button @click="updateToken">修改token2</button>

请求拦截器:

// 请求工具:请求拦截器(在请求头添加token)
request.interceptors.request.use(function (config) {// Do something before request is sent// config :本次 网络请求 的配置对象 请求地址/请求头/请求体...相关数据// config 里面有一个属性:headers// console.log(config)// 拦截检查本地vuex数据中是否有token,if (store.state.token) {config.headers.Authorization = `Bearer ${store.state.token.token}`}return config  // 返回配置对象
}, function (error) {// Do something with request errorreturn Promise.reject(error)
})

响应拦截器

// 添加响应拦截器
// 处理 transformResponse 处理之后数据
request.interceptors.response.use(function (response) {console.log(response)// 对响应数据做点什么??return response
}, function (error) {// 对响应错误做点什么??return Promise.reject(error)
})

记录滚动位置

给每个列表设置独自的滚动区域,而现在滚动区域是公共的body

.artcile-list {height: 79vh;overflow: auto;
}

今日头条切换页面缓存位置原理:组件缓存 : include=” “

目标:文章列表缓存  所有子组件都缓存下来,(无需再次网络请求,组件不销毁,占内存)<!-- <keep-alive :include="Layout"> --><router-view></router-view>    子路由出口<!-- </keep-alive> -->
// :include="需要缓存的组件名name"  --> "name1,name2,name3..."

1,来回切换的两个组件的上一级组件需要用标签包裹子路由出口,让需要缓存的那个组件缓存起来;'上一级组件’被销毁,则子组件缓存丢失。

2,需要缓存的子组件中:

  // 变为激活状态activated () {// 给.article-list元素(子组件的根元素标签,template标签下一级)赋值滚动条的位置数据this.$refs.listRoot.scrollTop = localStorage.getItem('scrollTop')},// 变为未激活状态deactivated () {},mounted () {// 监听滚动条的滚动,保存滚动条的位置this.$refs.listRoot.onscroll = function (e) {localStorage.setItem('scrollTop', e.target.scrollTop)}}    // 'scrollTop' 用和对应组件的特殊标识变量保存到本地,可以实现该子组件所有页面都独立缓存下来
------------------------------------------------------------------------
<style lang='less' scoped>
.article-list {// <!-- 目标:文章列表滚动条缓存 -->// 🆘🆘🆘给每个列表设置独自的滚动区域,而现在滚动区域是公共的bodyoverflow-y: auto;height: 93vh;
}
</style>

大数字精度失真问题:

后端返回 JSON 字符串格式的数据,经过 axios 转换成数据对象,但是 JavaScript 能够准确表示的整数范围在-2^532^53之间(不含两个端点),超出安全整数范围的 id 无法精确表示。

npm i json-bigint  //第三方包处理大数字的包
// https://www.npmjs.com/package/json-bigintconst jsonStr = '{ "art_id": 1245953273786007552 }'
// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转

通过 Axios 请求得到的数据都是 Axios 处理(JSON.parse)之后的,我们应该在 Axios 执行处理之前手动使用 json-bigint 来解析处理。Axios 提供了自定义处理原始后端返回数据的 API:transformResponse

// 请求拦截器--》transformRequest--》transformResponse--》响应拦截器
import axios from 'axios'
import jsonBig from 'json-bigint'const request = axios.create({baseURL: 'http://ttapi.research.itcast.cn/', // 接口基础路径// transformResponse 允许自定义后端返回的原始响应数据(字符串)transformResponse: [function (data) {try {// 如果转换成功则返回转换的数据结果return jsonBig.parse(data)} catch (err) {// 如果转换失败,则包装为统一数据格式并返回return {data}}}]
})export default request

后端返回文章正文样式调整:

github-markdown-css 样式文件下载到项目中

1,导入 import './github-markdown.css' 
2,添加类名 文章内容最外层标签 添加 'markdown-body'类名
3,'postcss-pxtorem'配置文件中配置忽略文件 exclude: 'github-markdown'

多层if-else判断:

标签配合v-if v-else

样式引入 css less引入:

数据在子组件,父组件需要获取并修改:

      // 方式一:在子组件props里面定义评论列表list,给定默认值,非必须,父组件通过父传子的方式也就获得子组件评论数据listprops: {commentList: {  // 评论列表type: Array,default: function () {return []}}}父组件:this.commentList.unshift(val.new_obj)// 方式二:通过ref获取子组件元素,再获取到子组件内的评论数组listthis.$refs.Aclist.list.unshift(val.new_obj)

方式一.png

provide / inject 祖先组件向后代传值:

一般越级传递,父子之间还是建议用

  • 类型
    • provideObject | () => Object 给所有后代组件提供数据
    • injectArray<string> | { [key: string]: string | Symbol | Object } 一个字符串数组,或一个对象,对象的 key 是本地的绑定名。

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。提示:provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

// 父级组件提供 'foo'
var Provider = {provide: {foo: this.commentId},data() {return: {commentId: "25478965"}}// ...
}// 子组件注入 'foo'
var Child = {inject: ['foo'],// inject: {          或者写成一个对象,可以来校验传递的数据//      foo: {//        type: [Number,String,Object],//        defalt: Null   默认值,非必须//      }// }
}created () {console.log(this.foo) // => "25478965"}// ...
}

v-model父子组件数据双向绑定:

🆘🆘🆘用v-model传递数据,监听同一数据。使用model属性,prop将数据名由value改为想要的,event将事件名input改为想要的名字。

v-model父子传值.png

Vant组件弹出层:

弹出层是懒渲染的,只有在第一次展示的时候才会渲染里面的内容。强制渲染可以采用v-if的方式。

动态路由props传参两种实现方式:

//点击 articleList里面 articleItem 带参数跳转
<van-cell :to="'/article/' + article.art_id">...</van-cell>
<van-cell :to="`/article/${article.art_id}`">...</van-cell>
<van-cell :to="{name:'article',params:{articleId:article.art_id}}">...</van-cell>
//路由配置里面:
const routes = [{其他路径 ...},{// 🆘🆘文章动态路由:path: '/article/:articleId',name: 'article',component: () => import('@/views/article/index.vue'),// 🆘🆘方法一:开启路由传参,将路由动态参数:articleId映射到组件的 props中 推荐这种做法props: true},其他路径 ...
]
//子组件中props正常接收:
export default {props: {// 动态路由传值。articleId: {type: [Number, String, Object],required: true}},// 方法二:data(){return articleId: this.$route.params.articleId  }
}

git操作:

1- 创建远端仓库XXXXX
2- 初始化仓库
前提:如果项目当中没有.git文件夹
命令: git init
vscode: 切换到源代码管理面板,点击初始化,选择项目
3- 提交代码到暂存区
命令:git add .
vscode:在更改添加+
4- 提交代码到本地仓库
命令: git commit -m '第一次提交'
vscode:在输入框输入注释,点击ctrl+enter
5- 推送到远程仓库
命令: git push origin master,git push https://gitee.com/huangjinlin/xxxxx.git master
vsocde:点击...,点击推送
注意第一次需要填写url,别名origin

image-20220321215146737

vue_cli本地代理解决跨域问题:

vue-cli的配置文件即**vue.config.js**,这里有我们需要的 代理选项

module.exports = {devServer: {// 代理配置proxy: {// 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制// localhost:8888/api/abc  => 代理给另一个服务器// 本地的前端  =》 本地的后端  =》 代理我们向另一个服务器发请求 (行得通)// 本地的前端  =》 另外一个服务器发请求 (跨域 行不通)'/api': {target: 'www.baidu.com', // 我们要代理的地址changeOrigin: true, // 是否跨域 需要设置此值为true 才可以让本地服务代理我们发出请求// 按需 路径重写pathRewrite: {// 重新路由  localhost:8888/api/login  => www.baidu.com/api/login'^/api': '' // 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做 }},}}
}

当加载的图片报错无法找到时,设置默认图片的方法:

方式一/二:利用原生方法 / vue中 this.$refs. ,获取图片的DOM元素后,通过onerror事件监听报错的图片,回调函数配置默认图片的src地址;

方式三:通过创建自定义指令,设置自动监听onerror事件,回调函数配置默认图片的src地址。

vue项目中package.json与package-lock.json作用及区别:

package-lock.json文件内保存了 node_modules中所有包的信息,包含着这些包的名称、版本号、下载地址。这样带来好处是,如果重新 npm install 的时候,就无需逐个分析包的依赖项,因此会大大加快安装速度。lock代表的是“锁定”的意思,用来锁定当前开发使用的版本号,防止npm install的时候自动更新到了更新版本。因为新版本可能替换掉老的api,导致之前的代码报错。

package.json 文件只能锁定大版本,也就是版本号的第一位,并不能锁定后面的小版本,你每次npm install都是拉取的该大版本下的最新的版本,为了稳定性考虑package-lock.json文件应运而生,所以当你每次安装一个依赖的时候就锁定在你安装的这个版本。当你执行npm install的时候,node从package.json文件读取模块名称,从package-lock.json文件中获取版本号,然后进行下载或者更新。

路由规则里面的hidden:

path: ’ ’ // 当二级路由的path什么都不写的时候 表示该路由为当前二级路由的默认路由

redirect [重定向]:
在父子嵌套结构中,父级的redirect指向子级children里的path

meta [元数据]:

其实就是存储数据的对象 我们可以在这里放置一些信息可以作用判断[用户是否已登陆]
可以通过meta值,展示[面包屑]

hidden 是否需要展示该路由[是否渲染该路由入口] 布尔值

functional为true,表示该组件为一个函数式组件:

函数式组件: 没有data状态,没有响应式数据,只会接收props属性, 没有this, 他就是一个函数

具名插槽的使用:

image-20220328140210006

批量注册全局组件:

批量注册全局组件.png

箭头函数的坑:

(a)=>{return b}   a=>b
// 1,不加{}自带return, 加{}必须要使用return
// 2,当需要箭头函数返回一个对象的时候,必须要()包裹{},否则{}会被当成箭头函数函数体的一部分
(a)=>({a:boo,b:baz})

v-for和v-if :

v-forv-if作用在不同标签时候,v-if包裹v-for时候,是先进行判断,再进行列表的渲染 ;

最终结论:v-for优先级比v-if

  1. 永远不要把 v-ifv-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
  2. 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
  3. 如果条件出现在循环内部,可提前过滤掉那些不需要显示的项,再循环。

data属性是一个函数而不是一个对象:

  • 根实例是单例对象时(其他组件为函数式组件),data可以是对象也可以是函数,不会产生数据污染情况
  • 组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象

一个项目有多个组件,每个组件都是一个单独的Vue实例对象,当多个实例对象打包合并时,对象式数据变量会相互影响/污染,所以不能是对象。

创建元素的三种方式:

创建元素的三种方式.png

document.write('<div>123</div>');inner.innerHTML += '<a href="#">百度</a>'var a = document.createElement('a');create.appendChild(a);

innerHTML字符串拼接方式(效率低)–> document.body.innerHTML += ‘

123

createElement方式(效率一般)

innerHTML数组方式(效率高)–>array.push( ) array.join(‘’);

Vue3:

reactive函数:

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

实现原理:

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

  • 通过Reflect(反射): 对源对象的属性进行操作。

        <script>//源数据let person = {name: '张三',age: 18}//模拟Vue3中实现响应式const p = new Proxy(person, {//有人读取p的某个属性时调用get(target, propName) {console.log(target, propName); // {name: '张三', age: 18}  'name'console.log(`有人读取了p身上的${propName}属性`);// return target[propName]return Reflect.get(target, propName)},//有人修改p的某个属性、或给p追加某个属性时调用set(target, propName, value) {console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)// target[propName] = valueReflect.set(target, propName, value)},//有人删除p的某个属性时调用deleteProperty(target, propName) {console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)// delete target[propName]return Reflect.deleteProperty(target, propName)}})</script>
    

扁平数据转树型结构:

/** **  将列表型的数据转化成树形数据 => 递归算法 => 自身调用自身 => 一定条件不能一样, 否则就会死循环*  遍历树形 有一个重点 要先找一个头儿* ***/
export function tranListToTreeData(list, rootValue) {var arr = []list.forEach(item => {if (item.pid === rootValue) {// 找到之后 就要去找 item 下面有没有子节点const children = tranListToTreeData(list, item.id)if (children.length) {// 如果children的长度大于0 说明找到了子节点item.children = children}arr.push(item) // 将内容加入到数组中}})return arr
}

.sync语法糖:

父组件给子组件传参,子组件使用并修改

// 父组件:
<AddDept v-if="showDialog" :show-dialog.sync="showDialog" ></AddDept>// 子组件关闭弹层:
this.$emit('update:showDialog', false)// 或者直接使用 this.$parent.showDialog = false 注意父级组件嵌套关系

some和every对空数组进行遍历时:

//对空数组使用some和every结果与预期不符合(也不符合逻辑)
[].some(item=>item===true)  //false
[].every(item=>item===true)  //true //every() 不会对空数组进行检测 若收到一个空数组,此方法在一切情况下都会返回 true//解决办法:
//在使用some和every之前,先对数组进行判断是否为空

权限管理:

1,登录权限控制:

依靠 Token 存入vueX 存入本地cookies

2,菜单权限管理:

router 路由守卫 里面执行:

  • 获取个人角色信息里面的页面标识menus ;
  • 过滤动态路由(往vueX里面加入动态路由,控制菜单栏显示隐藏);
  • addRoutes往静态路由里面合并动态路由以及 * 重定向到/404 ;
  • 再跳转一次 next(to.path)

权限管理.png

使用Mixin技术注入全局vue方法:

3,具体权限点points控制显示与隐藏:

常用于权限点的控制,控制页面具体按钮的显示与隐藏

// vueX混入公共函数
import store from "@/store";
// 在所有vue组件中混入钩子函数/方法等,以供调用
export default {created() {  // 不会影响单组件的crested钩子函数执行console.log("created-minxin");},methods: {checkPermission(point) {  // 某一个具体按钮的权限点为pointtry {return store.state.user.userInfo.roles.points.includes(point);} catch (error) {// 捕获错误,防止退出登录的时候报错return false;  // 🆘🆘🆘退出的时候先清除了个人信息,store.state.user.userInfo.roles变成undefined了,无法读取points,所以报错。}}}
};
-----------------------------------------------------------
// main.js里面引入--混入
//引入mixin
import checkPermission from '@/minxin_test/checkPermission'
//混入Vue
Vue.mixin(checkPermission)
-----------------------------------------------------------// 组件中使用:
this.checkPermission()  //直接使用(模板中使用无需this)

vueX和localStorage,sessionStorage,cookie的区别:

1.区别:

vueX 随运行程序存储在内存中,当刷新页面(这里的刷新页面指的是 --> F5刷新,属于清除内存了)时vueX存储的值会丢失。

localstorage(本地存储)则以文件的方式存储在本地硬盘中, 永久保存(不主动删除,则一直存在);

sessionStorage(会话存储), 临时保存,同源的窗口中始终存在,不怕刷新,但是页面关闭后就清除掉了。

localStorage和sessionStorage 不受页面刷新影响。只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理。

cookie:既可以在客户端设置也可以在服务器端设置。cookie会跟随任意HTTP请求一起发送,一般最多只能存储4KB的数据(随浏览器不同而变化)。cookie的内容主要包括:名字、值、过期时间、路径和域。路径与域一起构成cookie的作用范围。若不设置时间(存储在内存中),则表示这个cookie的生命期为浏览器会话期间(页面关闭清除), 设置时间(存储在硬盘中)。

2.应用场景:vuex(响应式的)用于组件之间的传值,localstorage,sessionstorage,cookie则主要用于不同页面之间的传值。(非响应式的,页面刷新才会更新)

总结:vuex怕F5刷新,sessionStorage怕关闭页面,cookie不设置过期时间时怕关闭页面,设置有过期时间就和localstorage一样啥都不怕。

————————————————

$store和store的区别:

$store 是挂载在 Vue 实例对象原型上的(即Vue.prototype),而组件也其实是一个Vue实例,在组件中可使用 this 访问原型上的属性,template标签 拥有组件实例的上下文,可直接通过 {{ $store.state.userName }} 访问user模块中 userName变量,等价于 script标签 中的 this.$store.userName。
至于 {{ store.state.userName }},script 中的 data 需声明过 store 才可访问,一般在组件中引用过后使用。

Node-Sass does not yet support your current environment

BUG解决办法:

产生问题的原因

执行npm install命令时,其实是npm按照项目里的package.json文件来下载项目所有的依赖;

由于每个人的电脑环境等不同的问题,有些依赖会不支持当前的环境;

解决方案

先卸载之前的node-sass,然后再安装一遍node-sass就可以完美解决了,npm会自动智能的选择最新的并且支持本地环境的依赖;

特别注意

node-sass被墙了,使用npm会下载失败,所以请用淘宝镜像cnpm下载或者使用翻墙软件下载;

//先卸载node-sass
npm uninstall node-sass//用cnpm重新安装node-sass
cnpm install node-sass -D

登陆login处理函数 登录过程1

validate对整个表单进行验证 登录过程2

发起action登录的异步请求 登录过程3

vuex中actions发登录请求 登录过程4

登录接口 到请求拦截器 登录过程5

axios设置请求拦截器 设置请求头token 判断是否失效 登录过程6

axios设置响应拦截器 结构数据 登录过程7

在vuex中保存token 同时保存到本地 登录过程8 跳转首页

前置守卫 登录过程10

1,获取菜单控制的标识menus

2, 过滤动态路由

3,添加动态路由

4,解决404问题

TypeScript:

//01,安装ts:
npm install -g typescript//02,查看版本:
tsc -V//03,VSCode编译
//手动编译:tsc 文件名.ts
//自动编译:
// 1). 生成配置文件tsconfig.jsontsc --init
// 2). 修改tsconfig.json配置"target": "ES5", //编译目标js版本"outDir": "./js", //指定输出文件夹"strict": false, //是否严格模式
// 3). 启动监视任务:终端 -> 运行任务 -> 监视tsconfig.json//04,类型注解:
// 变量: 变量类型|变量类型... (首字母小写/string/boolean/any任意类型/unknown未知类型) 建议用unknown
// any类型的变量会影响其他变量,unknown类型的变量不会影响其他变量,是一个类型安全的any
// 声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式的any)
let c1 : boolean | string;//也可以使用 | 来连接多个类型(联合类型)
let b1: "male" | "female";let c: boolean = false;
// 如果变量的声明和赋值时同时进行的,TS可以自动对变量进行类型检测
let c= false;
c = true; // 可以赋值
c = 123; // 报错   //也可以直接使用字面量进行类型声明 但是后期不可修改有点儿类似常量了
let a1: 10;//默认情况下(非严格模式) null 和 undefined 是所有类型的子类型。就是说你可以把 null 和 undefined 赋值给 number 类型的变量//数组定义方式1 
//let 变量名:数据类型[] = [值1,值2,值3...]
let arr1: number[] = [10,20,30,40,50,60]//数组定义方式2 泛型的写法
//let 变量名:Array<数据类型> = [值1,值2,值3...]  
let arr2: Array<string> = ['asd','zxc','qwe']//数组定义方式3 元组Tuple类型的写法 限定对应关系
//let 变量名:[数据类型,数据类型,数据类型] = ['小甜甜',100,true]  //数组定义方式4 let 变量名:any[] = ['小甜甜',100,true...] 数组元数个数不确定,类型不确定时//枚举类型:enum 元素的编号(数据)省略时,默认从0开始,也可以手动赋值
enum Color {Red = 333,Green = 2,Blue = 4
}
let c: Color = Color.Green  // 2
let c2: Color = Color[4]  // Blue//定义一个函数参数是object类型,返回值也是object类型:
function fn2(arg: object): object {console.log('fn2()', arg)//return 'abc' //报错return {}
}// void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void
/* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值,意义不大。 */
function fn(): void {console.log('fn()')// return undefined// return null// return 1 // error
}//05,类型断言: 值类型有多种情况的时候,可以用来手动指定一个值的类型
//语法方式1: <类型>值
//   方式2: 值 as 类型  tsx中只能用这种方式
let x: boolean | string; //声明了未赋值
if ((<string>x).length) {return (x as string).length}

//06,类 和 接口://定义一个类class User {firstName: stringlastName: stringfullName: stringconstructor(firstName: string, lastName: string) {this.firstName = firstNamethis.lastName = lastNamethis.fullName = firstName + '_' + lastName}}//由 User这个类 实例化一个对象(约束一个实参)let user = new User('shi', 'hui')console.log(user); //User {firstName: 'shi', lastName: 'hui', fullName: 'shi_hui'}//定义一个接口(约束形参):是一种类型,一种规范,一种规则,一种约束...interface Person {readonly firstName: string  //接口的只读属性lastName?: string           //接口的可选属性}//定义接口函数function getFullName(person: Person) {return 'hello' + '_' + person.firstName + '_' + person.lastName}console.log(getFullName(user)); //hello_shi_hui
/*
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)
接口类型的对象多了或者少了属性是不允许的可选属性: 属性后面加 ?只读属性: 属性前面加 readonly
*///07,函数类型接口:通过接口的方式作为函数的类型来使用  接口可以描述函数类型(参数的类型与返回的类型)
interface SearchFunc {(source: string, subString: string): boolean
}
const mySearch: SearchFunc = function (source, sub) {return source.search(sub) > -1 //类似于source.indexOf(sub)
}
//等同于:
const mySearch = function(source: string, sub: string): boolean {return source.search(sub) > -1
}console.log(mySearch('abcd', '1')) //false//08,类类型接口-->类实现接口,TS能够用接口来明确的强制一个类去符合某种契约。
interface Alarm {alert(): any
}interface Light {lightOn(): voidlightOff(): void
}class Car implements Alarm, Light {alert() {console.log('类和接口之间叫“实现”!类实现接口用implements关键字,多个接口用,逗号')}lightOn() {console.log('123')}lightOff() {console.log('456')}
}//接口与接口之间也可以相互继承,使用extends关键字,逗号连接多个接口,{ }结尾
interface LightAndAlarm extends Alarm, Light { }

//09,类的继承与多态
class Animal {name: stringconstructor(name: string) {this.name = name}run(distance: number = 0) {console.log(`${this.name} run ${distance}m`)}
}class Snake extends Animal {constructor(name: string) {// 调用父类型构造方法super(name)}// 重写父类型的方法run(distance: number = 5) {console.log('sliding...')super.run(distance)}
}class Horse extends Animal {constructor(name: string) {// 调用父类型构造方法super(name)}// 重写父类型的方法run(distance: number = 50) {console.log('dashing...')// 调用父类型的一般方法super.run(distance)}xxx() {console.log('xxx()')}
}const snake = new Snake('sn')
snake.run()
//sliding...
//sn run 5mconst horse = new Horse('ho')
horse.run()
//dashing...
//ho run 50m// 父类型引用指向子类型的实例 ==> 多态
const tom: Animal = new Horse('ho22')
tom.run()
//dashing...
//ho22 run 50m/* 如果子类型没有扩展的方法, 可以让子类型引用指向父类型的实例 */
const tom3: Snake = new Animal('tom3')
tom3.run()
//tom3 run 0m/* 如果子类型有扩展的方法, 不能让子类型引用指//实例 */
// const tom2: Horse = new Animal('tom2')  报错
// tom2.run()

//修饰符(类中成员的修饰符)

http://www.ppmy.cn/news/531477.html

相关文章

惠普服务器故障代码_hp常见错误代码

惠普系列激光打印机,大多数通过面板的液晶显示,可以迅速找出故障原因,下面列表为常见的错误代码及其解释。 1.常见错误代码 显示信息 信息解释 解除方法 13.1 paper jam或 13.2 paper jam 打印纸在送纸部分被延迟或停止 1) 确定纸盒被正确安装; 2) 确定送纸部分没有阻碍物;…

完美解决“惠普p1007打印机老显示脱机使用”的问题

写在这里的初衷&#xff0c;一是备忘&#xff0c;二是希望得到高人指点&#xff0c;三是希望能遇到志同道合的朋友。 目录 一、问题二、原因1.常见原因2.根本原因 三、解决办法 一、问题 惠普p1007打印机老显示脱机使用 二、原因 1.常见原因 通过上网查找&#xff0c;发现原…

Mac如何使用Windows各种老式打印机(P1007为例)

文章目录 前言前提设备 正文Windows设置安装打印机驱动确保打印机可以正常链接使用windows共享设置打印机共享设置设置打印专用登陆账户记录ip地址&#xff08;可选&#xff09;固定ip Mac设置下载打印机相关驱动安装打印机驱动添加打印机测试打印&#xff1a; 打印机硬件设置补…

第五十一章 协助调查

眼前一个红彤彤的东西缓缓升起。 旭日东升&#xff1f;可现在才升未免太晚了些&#xff0c;升起的速度也未免太快了些&#xff0c;这红日么&#xff0c;也未免太小了些&#xff0c;而且&#xff0c;刚升起的朝阳&#xff0c;也未免显得太红太亮了些。 “是谁呀&#xff0c;水烧…

Windows XP 安全更新程序 (KB958644)

Windows XP 安全更新程序 (KB958644) 现已确认存在一个安全问题&#xff0c;未通过身份验证的远程攻击者可能会利用此问题危及基于 Microsoft Windows 的系统的安全并获取对该系统的控制权。 http://www.microsoft.com/zh-cn/download/details.aspx?id3205 此更新发布时间…

MS08-067(kb958644)紧急漏洞与补丁

2008年10月23日,微软爆出特大安全漏洞,几乎影响所有Windows系统,强烈建议广大用户及时下载安装该漏洞补丁。 成功利用该漏洞的远程***者,可能会利用此问题危及基于Microsoft Windows系统的安全,并获取对该系统的控制权。 这是微软近一年半以来首次打破每月定期发布安全公告…

微软最牛MS08-067漏洞各系统补丁下载地址

本次MS08-067严重漏洞各系统补丁地址如下&#xff1a; 中文操作系统KB958644补丁下载地址&#xff1a; Windows Vista 安全更新程序 (KB958644) http://download.microsoft.com/download/d/c/0/dc047ab9-53f8-481c-8c46-528b7f493fc1/Windows6.0-KB958644-x86.msu Windows Serv…

MyBatis源码面试题

一、介绍下你对MyBatis源码的理解 MyBatis是一款优秀的Java ORM框架&#xff0c;其核心是实现了对关系型数据库的操作&#xff0c;它的源码实现主要集中在以下几个方面&#xff1a; 映射器解析 MyBatis采用了XML和注解两种方式来定义SQL语句和映射器。在源码中&#xff0c;主要…