前端学习笔记

jquery

jquery插件

网上搜索jquery插件,点击这里
## swiper插件
网上搜索swiper插件,点击这里

wow.js 滚动到此处的动画

网上搜索wow.js下载,进入官网,点击这里

moment 插件

网上搜索moment.js,进入官网点击这里

// 这种包是基础的
// <script src="./js/moment.js"></script> 
// 这种包是基础并且多语言的,两者引其一
<script src="./js/moment-with-locales.js"></script>
moment.locale('zh-cn');
console.log(moment().format('dddd'))
console.log(moment("2020-03-29T00:00:00.000Z").fromNow())
console.log(moment("2020-03-29T00:00:00.000Z").format('YYYY年MM月DD日'))

原生js一些知识

js基础知识

  • 工具库 1 3
  • 原声 js ECMASCRIPT ES3 ES5 ES6 ES…
  • 严格区分大小写
  • 基础知识
  • 变量:存储数据的容器
  • var ind=$(‘.list li’).index()
  • 声明 必须使用var声明变量 一般不能重复声明
  • 赋值: =

js 命名规范:

  1. 语义化
  2. 只能以字母,下划线 $ 开头,只能包含字母下划线 $
  3. 不能以关键字和保留字命名(js语法用到的关键字 if var,要在下一个版本当做关键字的保留字)
  4. 驼峰方式命名(多个单词组合)liIndex
  5. js 里面所有命名都是上述规范

js的赋值错误

  1. 变量声明但没赋值就console、变量先console,再声明赋值(undefined)
  2. 没声明就console(num is not defined)
  3. 变量提升,声明过程提升到最顶端 赋值过程不提升
       console.log(num1)
       var num1=10   
       // (undefined),声明提升
       ```
    
  4. TypeError: Cannot read property ‘grade’ of undefined
    var a = undefined
    a.grade
    // 不能设置undefined的grade属性,例子不好,马马虎虎看吧
    
  5. xx of null :意思是 xx 前面的是一个空对象 null, (typeof null)->object

js 里的数据类型

  1. Number
  2. string
  3. boolean
  4. undefined
  5. null
  6. object
  7. symbol

数据类型之间的转换

  1. 转换成数字
    • parseInt(值) (转成整型)
    • parseFloat(值) (转成浮点型 转换方式一个一个字符的转换,直到不能转换为止)
    • Number(值) (直接整体转换 转换失败得到NaN)
    var str='15.6af'
    var int=parseInt(str)
    var float=parseFloat(str)
    var num=Number(str)
    console.log(int,float,num)
    //15,15.6,NaN
    //NaN(not a number) 不是一个数字的数字 数学计算出错
    
  2. 转换成字符串
    • String(值)
    • 值.toString()
    var num = 15.87
    var str = String(num)
    var str1 = num.toString()
    console.log(typeof str, typeof str1);
    // string string
    // 数据类型的检测:typeof
    
  3. 转换成布尔值
    • Boolean(值)
    var num = ''
    var bool = Boolean(num)
    console.log(bool)
    // false
    // false:0,'空',undefined,null,NaN
    // true:'空格',数字
    
  4. 隐士转换
    • if(判断条件):和以上规则一样,其余都是true
    // 赋为任意值就为真,赋为 0,'空',undefined,null,NaN,就为假
    if(num = 10){
        console.log(1);
    }
    
    • ‘+’ :左右只要出现字符,就会进行拼接操作
    • ‘- / * %’ :这些符号左右的所有值都会默认转换成数字在进行数学运算 转换方法是Number
    var a = '6'
    var b = '7a'
    console.log(a % 5, b * 5);
    // 1 , NaN
    

运算符

  • ‘+ - / * %’
  • ‘> < == >= <= != === !==’
  • ===和 == 区别:前者比较的是值和数据类型,后者只比较值
  • 同上 != 和 !== 意思一样 1true, 0false
  • || && ! (或 与 非)
  • 以后所有的判断相等都使用 === 判断(老师要求)
var num = 10
var str = '10'
console.log(num == str, num === str)
console.log(num != str, num !== str)
// true,  false
// false, true

循环

  • if else if 判断
  • for 循环:break 结束循环,continue 结束本次循环,继续下次循环
  • switch 循环:找到第一个满足条件的,有break就结束,没有就继续执行
var day = '1'
switch(day) {
    case '1': console.log('今天星期一');break;
    case '2': console.log('今天星期二');break;
    case '3': console.log('今天星期三');break;
    default: console.log('今天放假啦!');
}
// 今天星期一
var day = '2'
switch(day) {
    case '1': console.log('今天星期一');
    case '2': console.log('今天星期二');
    case '3': console.log('今天星期三');
    default: console.log('今天放假啦!');
}
// 今天星期二 今天星期三 今天放假啦!
// break 才跳出循环,不然继续执行到最后
  • while 循环
// 输出 1-10
var i = 0
while(i < 10){
    i++
    console.log(i);
}
  • do while 循环
// 输出 1-10
var i = 0
do {
    i++;
    console.log(i);
}
while (i<10)

js 函数的知识

函数定义

  • 装代码块的地方。 装功能的地方
  • 形参:函数定义的时候设置参数
  • 实参:调用的时候传递的参数
  • 功能实现之后需要将运算的结果暴露到函数外面,需要设置函数的返回值
  • 直接在函数的内部使用 return关键字将需要的值显示
  • return 关键字有两个作用 1.返回值 2.跳出函数
  • 函数的作用域分为两种:
    1. 全局作用域(整个页面,引入的其它js里定义的变量也是全局,任何地方都可以使用)
    2. 局部作用域(函数内,这里定义的变量或者函数只有局部作用域和子作用域可以使用,兄弟作用域也拿不到)
    function fun() {
        var a = 200
        function xxx(){
            var b = 100
            console.log(a);
        }
        xxx() // 必须在里面调用才可以
        console.log(b); // 这里出错,因为 b 在它的子函数内
    }
    fun()
    xxx() // 这里出错,因为xxx函数不是全局的
    
    function fun(){
        num = 100
    }
    fun() 
    console.log(num)
    // 这里可以输出100,因为fun里没写var,就默认是全局的变量
    
  • 一个功能使用了两次及以上就需要将功能封装到函数内

函数创建

  1. 函数式创建
    function fun(){
        console.log('毕业见!!')
    }
    fun()
    
  2. 变量式创建
    var fun = function () {
      console.log('毕业见!!')
    }
    fun()
    

js 对象

对象的基础知识

  • 对象 object 属性的无序集合
  • 属性名:属性值,…
  • 方法:当属性的属性值是函数的时候称这个属性叫做方法

自定义对象

var goods = {
    goodsName:'realme 5x',
    goodsPrice:3000,
    say:function(){
        console.log('我是新品')
    }
}

获取对象的属性

// 对象.属性名 就是获取对象的属性值
console.log(goods)
console.log(goods.goodsName)
goods.say()
// 修改 直接对属性进行重新赋值
goods.goodsName='realme 6x'
goods.say = function(){
    console.log('我是最新品')
}

基础类型 复杂类型object

  • 两者在浏览器内的存储方式不同
  • 基础类型在浏览器中存储的就是值本身
  • object 在浏览器中的存储的是地址,每次创建新对象的时候都会开辟一个新的地址存储
// 此例子对应的不是上面的,而是我认为的难点!
var obj = {
    name:'小张'
}
var obj1 = obj // 拷贝了一个地址
obj1.name = '小王'
console.log(obj.name); //小王
console.log(obj1 === obj); // true
  • object :自定义对象 内置对象(日期,数学,…) 浏览器对象(定义的全局变量和全局函数全是window的)

内置对象,数组

一些定义

  • 数组:数据的有序集合
  • 对数组进行全面的处理需要知道数组对象内的所有属性和方法
  • 内置对象内的属性和方法都是定义好了的,绝对不能进行修改只能使用
  • 数组 属性 length 获取数组的长度(元素个数)
  • 学习方法的网址mdn web

学习 ‘方法’

  • 方法是哪个对象的(给谁用的)
  • 方法的作用
  • 返回值是什么
  • 修不修改原来的对象

数组的方法

  1. push:向数组末尾添加一个或多个元素,返回新长度, 原来的数组改变了
var arr = [1, 2, 3]
var length = arr.push(5)
console.log(arr,length)
// [1,2,3,5] 4
  1. pop 删除数组内的最后一个元素,返回被删除的元素,原来的数组改变了
var num = arr.pop()
console.log(arr, num)
// [1,2,3] 5
  1. unshift 向数组开头添加一个或多个元素,返回新长度,原来的数组改变了
var length1 = arr.unshift(0, 1)
console.log(arr, length1)
// [0,1,1,2,3] 5
  1. shift 删除数组内第一个元素,返回被删除的元素,原来的数组改变了
var num1 = arr.shift()
console.log(arr, num1)
//[1,1,2,3] 0
//shift括号里填任何值都无用
  1. concat 数组拼接,返回结果,原数组不变
var arr1 = [8, 9]
var arr2 = arr.concat(arr1)
console.log(arr, arr2)
//[1,1,2,3] [1,1,2,3,8,9]
  1. splice 对数组的元素进行删除或添加,返回被删除的元素组成的数组,原来的数组改变了
//arr.splice(a,b,c)  a添加或删除的开始位置   b 删除的个数   c 添加的元素
var newArr = arr.splice(0, 2, 9)
console.log(arr, newArr)
// [9,2,3]  [1,1]
  1. slice 数组的截取,返回截取的数组,原数组不变
//arr.slice(a,b)  a,b 代表的都是位置   原则包括 a 不包括 b
var newArr1 = arr.slice(0, 2)
console.log(arr, newArr1)
//[9,2,3] [9,2]
  1. indexOf 查看数组内是否存在某个元素,返回元素的位置 或 -1 ,原数组不变
var ind = arr.indexOf(10)
var ind1 = arr.indexOf(3)
console.log(arr, ind, ind1)
//[9,2,3] -1  2 
  1. includes 查看数组内是否存在某个元素返回 true false,原数组不变
var bool = arr.includes(8)
var bool1=arr.includes(9)
console.log(arr, bool,bool1)
//[9,2,3] false true
  1. reverse 颠倒数组顺序并返回,原数组改变了
var newArr2 = arr.reverse()
console.log(arr, newArr2)
//[3,2,9] [3,2,9]
  1. join 将数组拼接成字符串并返回,原数组不变
//arr.join('') 可加任何符号,都用来拼接
var str = arr.join() 
// 以逗号拼接, 3,2,9
var str=arr.join('')
//直接拼起来  329
var str=arr.join('--')
// 3--2--9
  1. sort 对数组按字符串进行升序排序,返回结果,原数组改变
var arr=[12, 4, 23, 564, 8, 41]
var newArr3 = arr.sort()
console.log(arr, newArr3)
//[12,23,4,41,564,8]

objArr.sort(function (a, b) { return a.likes - b.likes })
// 对对象进行升序排序, b-a 是降序
  1. every 遍历数组每找到一个元素执行里面的函数 当所有函数的返回值都是 true 的时候 every 返回 true 否则返回 false
var arr1 = [1, 2, 3, 4, 5]
var arr = arr1.every(function (ele, index, array) {
    return ele > 0
})
console.log(arr)
// true
  1. find findIndex 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined,有点类似 indexOf() 区别前者可以对对象类型数组进行查找后者不可以,
var goods = goodsArr.find(function (ele, index, array) {
    return ele.goodsPrice > 1000
})
console.log(goods)
// find找到的是大于1000的对象
// findIndex找到的是大于1000的那个对象的索引
var a = [9, 2, 3]
var a1 = a.find(function (ele) {
    return ele > 2
})
console.log(a1);
// 9,返回满足条件的第一个值
// findIndex找到的是满足条件的值的索引,找不到返回 -1
  1. map 映射,根据原数组生成一一对应的新数组
var arr1=[1,2,5]
var arr2 = arr1.map(function (ele, index, array) {
    return ele * 2
})
console.log(arr2)
// [2,4,10]
// 函数内的返回值会被当作新的数组内的值
  1. forEach 遍历数组每找到一个元素执行里面的函数,没有返回值,返回 undefined
var arr1 = [1, 2, 5]
arr1.forEach(function (item, index, array) {
    console.log(item)
})
// 1 2 5
// 大多情况用来遍历对象数组
  1. filter 遍历数组每找到一个元素执行里面的函数当函数的返回值时 true 就被留下,返回新数组,原数组不变
var newArr = arr1.filter(function (item) {
    return item > 1
})
console.log(newArr)
// [2,5] 返回新数组
  1. some 判断数组中有没有满足条件的一个或多个,返回布尔值, 原数组不变(和includes有点像)
var arrObj = [
    {
        name: '小黑',
        age: 18
    },
    {
        name: '小黑1',
        age: 21
    }
]
var bool = arrObj.some(function (ele) { return ele.age > 20 })
console.log(bool)

字符串的方法

  1. charAt 获取某个下标的字符
var str = 'joijhadsa111111'
console.log(str.charAt(5))
// a
  1. charCodeAt 获取某个下标的字符的字符编码
var str = 'bajoijhads111111'
console.log(str.charCodeAt(1))
// 97
  1. replace 字符串替换,没有能替换的就不替换,replace默认只替换第一个,加个g都替换
var str = 'ajoijhads111111'
var newStr = str.replace('joi', '')
var newStr1=str.replace('jo','55')
console.log(newStr,newStr1)
// ajhads111111  a55ijhads111111
var newStr2 = str.replace(/[a-z]/g, '5') 
// 555555555111111 把所有字母换成了5,加了 g 变成全局替换了
  1. match 查看字符串是否满足某个条件,返回满足条件的子字符串组成的数组,match也是只找第一个,要加g,i是不区分大小写
// 今天天气不错,最高气温20℃,最低气温7℃。
var str = 'joia1111v11'
console.log(str.match(/[a-z]/g))
// ['j','o','i','a','v']
var str1 = '今天 17 号天气不错,最高气温-20℃,最低气温-7℃。'
// (?=xx) 后面紧跟着 xx
console.log(str1.match(/-?[0-9]{1,2}(?=℃)/g))
// ["-20", "-7"]
  1. slice 字符串的截取
var str = 'ajoijhads11111v1'
console.log(str.slice(0, 3))
// ajo
  1. split 字符串拆分成数组,按照split的参数拆分,括号里什么都没有,就不拆分,括号里有’',就一个一个拆分
var str2 = '1-2-345-6'
console.log(str2.split('-'))
// ["1", "2", "345", "6"]
  1. toFixed 将数字变成字符串并且使用四舍五入的方式保留几位小数,原值不变
var num=0.1+0.2
console.log(num)
// 浮点型运算 运算的误差 0.000000000004 左右   四舍五入可以去掉
 var num1 = num.toFixed(2)
 console.log(num1,num)
 // 0.30  0.30000000000000004
  1. trim 将字符串前后的空格去掉,原值不变
var str = '   sadef   '  
console.log(str, str.trim())
// '   sadef   ','sadef'   
  1. 字符串新增方法:startsWith,endsWith(参数字符串是否在原字符串的头尾部),repeat,trimStart,trimEnd(消除头部空格)

  2. Math.random() 0-1 的随机数,包括 0 ,不包括 1

  3. Math.floor(3.94) 3 下舍

  4. Math.ceil(3.94) 4 上进

  5. Math.round(4.5) 5 四舍五入

真正的数字数组排序

var currentArr = [12, 4, 23, 564, 8, 456, 78, 24]
var resArr = currentArr.sort(function (a, b) {
    return b - a  //降序
})
console.log(resArr)
//[564, 456, 78, 24, 23, 12, 8, 4]

对象数组内的数字排序

var articleArr = [
      {
        title: 'react1',
        author: '小王1',
        likes: 200
      },
      {
        title: 'react2',
        author: '小王2',
        likes: 100
      },
      {
        title: 'react3',
        author: '小王3',
        likes: 150
      }
    ]

    var resArr1 = articleArr.sort(function (a, b) {
      return a.likes - b.likes
    })
    console.log(resArr1)
    //结果是按likes升序的对象数组
    

模板字符串,普通字符串拼接

for (var i = 0; i < goodsArr.length; i++) {
      // 普通的字符串拼接
      var goodsDiv = $('<div class="goods-wrap"><img src="' + goodsArr[i].goodsPic + '" /></div>')
      // 模板字符串
      var goodsDiv = $(`<div class="goods-wrap"><img src="${goodsArr[i].goodsPic}" /></div>`)
} 

内置对象 日期对象

  • 创建 当前(页面读取这段代码的那个时间)日期对象,必须new一个date才能用后面的
  • 将下面的所有方法加上 UTC 就是获取的世界时间(date.getUTCSeconds)
  • 将下面的所有方法get 换成 set 就是设置时间(一般没用)
var date = new Date()
date.setFullYear(2015)
console.log(date)
//此时年是2015年
var date = new Date()
//Sun Aug 02 2020 21:42:00 GMT+0800 (中国标准时间)
var year = date.getFullYear()
//年
var month = date.getMonth() + 1
// 默认月 0-11
var hao = date.getDate()
//getDate  几号
var day = date.getDay()
//getDay 星期 1-0(周日是0)
var hour = date.getHours()
// 时 
var minute = date.getMinutes()
// 分
var second = date.getSeconds()
// 秒
var milliSecond = date.getMilliseconds()
// 毫秒
var times = date.getTime()
// getTime  格林威治时间 1970.1.1  00:00:00  距现在时间的毫秒数    用于创建一个永远不会重复的数

setInteval 计时器

  • setInteval jquery的动画方法属于异步
  • 同步阻塞,异步非阻塞
  • 异步操作永远会在同步操作之后执行
  • 同步是按顺序往下执行
var run = setInterval('update()', 1000);
var num = 0
function update() {
    num++
    if (num == 5) {
        //clearinteval只终止定时器不终止函数
        clearInterval(run)
        // return 想终止函数可以用return
    }
    console.log(num);
}

setTimeout

//setTimeout 只执行一次,而setInterval执行多次
function hello() {
    console.log("setTimeout");
}
//使用方法名字执行方法
var t1 = setTimeout(hello, 1000);
var t2 = window.setTimeout("hello()", 3000);//使用字符串执行方法
window.clearTimeout(t1);//清除定时器

setTimeout 和 setInteval

//两个一起用一般是用来倒计时的,最后显示0,再变成别的
var run = setInterval('update()', 1000);
var num = 5
function update() {
    num--
    $('.box').text(num);
    if (num == 0) {
        clearInterval(run)
        setTimeout(function () {
            $('.box').text('结束');
        }, 500)

    }
}

filter 拆开的例子

var arr = [1, 2, 3]
function fun1(ele) {
    return ele >= 2
}
var arr1 = arr.filter(fun1)
console.log(arr1)
// [2,3]

正则表达式

  • 检测字符串的一个规则
  • .test -->返回Boolean .exec -->返回Object 详细结果 null
  • []一位 [0-9]一位数字 [a-z]一位小写字母 [A-Z]一位大写字母 [A-z]一位字母或下划线
  • {} 在某一个字符后面加上{} 代表这个字符的位数 {n} {n,m} {n,}
  • {0,1} === ? {0,} === * {1,} === +
  • ^开头 $结尾
  • | 或 ()先运算
  • 正则内想要写一些原本正则里面有意义的字符 需要加上转义符 5
  • 汉字的匹配
    var re = /[\u4e00-\u9fa5]/
    console.log('\u4e00', '\u9fa5')
    console.log(re.test('哈'))
    // 一 龥(笔画最多和最少的)  
    // true(所有汉字)
    
  • 创建带变量的正则
    var str = '1231'
    var re = new RegExp('^' + str + '$')
    console.log(re)
    //   /^1231$/
    

正则表达式的使用

// .test -->返回Boolean     .exec  -->返回Object 详细结果  null
var re = /bai/
var str = 'hello xiaobai'
console.log(re.test(str))
// true 正则.test(变量)

var re = /[abc][def]h/
// 匹配三个连续的小写字母
var re1 = /[A-z0-9]/
// 匹配一位字母或下划线或数字
var re = /^a+$/
// 匹配以a开头和结尾的多个a
var re = /^[0-9]{3}@(qq|QQ).com$/
// 345@QQ.com 3个数字开头.com结尾
var re = /^1[3678][6789][0-9]{8}$/
// 第一位是1,第二位是3678,第三位是6789,第四到第11位是数字

var allStr = '12655212 5412'
var replaceStr = '12'
var re = new RegExp(replaceStr)
console.log(allStr.replace(re,''))
// 655212 5412 :只替换第一个12
var re = new RegExp(replaceStr,'g')
// 6552 54 :替换所有12
var re = new RegExp(replaceStr + '(?= )','g')
// 126552 5412 : 替换12后面带个空格的,?=是条件,g是全局

正则的一些规则

字符串的长度str.length

  • \d 等价于[0 - 9] 一位任意数字字符
  • \D \d的反义 一位任意非数字字符
  • \w 等价于[A - z] 一位任意字母下划线
  • \W \w 的反义
  • \s 一位任意空白符(空格或者回车)
  • \S \s 的反义
  • 中括号里面的 ^ (不好理解看下面例子)
var re = /[abc]/  
//这个正则的意思是一位 a 或 b 或 c
var re1 = /[^abc]/ 
//这个正则的意思是一位非(a或b或c)的字符
var re2 = /[^0-9]/ 
//这个正则的意思是一位非数字的字符

原生 dom document

  • 浏览器对象 提供了js访问页面的一些接口(方法)
  • document 整个文档(页面) 还是一个对象该对象下存储了很多方法用来操作页面
    console.log(window.document)
    // 输出的是整个页面的代码
    
  • window 的一些方法 setInterval setTimeout console alert(弹出)…
  • 其实我们写的所有的全局变量和全局函数都是属于浏览对象的给浏览器对象创建的 全局变量相当于 window 的属性 全局函数相当于 window 的方法
    var num = 10
    console.log(window.num)
    function fun() {
        console.log(11)
    }
    window.fun()
    // 10   11
    

document 方法

```js
document.getElementById('id') 
// 通过 id 名获取 **只能获取一个**
document.getElementsByTagName('标签名')
// 通过标签名获取 获取的一个**类数组** 里面存的是真正的节点
document.getElementsByClassName('class名') 
// 通过class名获取 获取的一个**类数组** 里面存的是真正的节点
document.getElementsByName('name名') 
// 通过name名获取 获取的一个**类数组** 里面存的是真正的节点
document.querySelector('.box') 
// 通过css选择器获取 **只能获取第一个**
document.querySelectorAll('css 选择器')
// 通过css选择器获取 获取的一个**类数组** 里面存的是真正的节点
```

document 获取和设置属性

  • getAttribute setAttribut(和 jquery 的 attr 类似)
  • 用法 getAttribute(‘属性名’) setAttribute(‘属性名’,属性值)
    var btnArr = document.querySelectorAll('.btn')
    for (var i = 0; i < btnArr.length; i++) {
        document.querySelectorAll('.btn')[i].onclick = function () {
            console.log(this.getAttribute('data-index'))
            this.setAttribute('title', '哈哈哈')
            document.querySelector('.box').style.width = '500px'
        }
    }
    
  • 获取和修改和添加 div 的类名
    // 获取 div 的类名字
    document.querySelector('div').className
    // 修改 div 的类名字
    document.querySelector('div').className = '另一个类名'
    // 添加 div 的类名字
    document.querySelector('div').className = document.querySelector('div').className + ' 另一个名字'
    

document 事件

  • 原生dom对象.onclick = function(){}
  • onclick onmouseenter onmouseleave onblur onfocus …
    document.querySelectorAll('.btn')[0].onclick = function () {
        document.querySelector('.box').style.width = '500px'
    }
    

创建 dom 节点

document.createElement('标签名) $('<li></li>')

添加 dom 节点

append 或 appendChild a.appendChild(b) a是父级 b是子级
insertBefore a.insertBefore(b,c) a是父级 b是要添加的 c是a中存在的,在c前面加b

删除 dom 节点

a.removeChild(b) a是父级 b是子级 b.remove()

修改 dom 节点

属性:innerText(不可以解析标签) innerHTML(可以解析标签)
document.querySelecor(‘.box’).innerHTML = ‘111’

表单的处理

属性:
value 获取或者设置 text password textarea select 的值
checked 获取或者设置 checkbox radio 的状态 true false
方法:focus() blur()

// 刚进页面就获取焦点了,jquery也这样
document.querySelector('input').focus()

常用属性

// 1  element.id       设置或返回元素的 id。
// 2  element.innerHTML   设置或者返回元素的内容,可包含节点中的子标签以及内容
// 3  element.innerText  设置或者返回元素的内容,不包含节点中的子标签以及内容
// 4  element.className   设置或者 返回元素的类名
// 5  element.nodeName    返回该节点的大写字母标签名
// 6  element.nodeType   返回该结点的节点类型,1表示元素节点  2表示属性节点……
// 7  element.nodeValue  返回该节点的value值,元素节点的该值为null
// 8  element.childNodes  返回元素的子节点的nodeslist对象,nodelist类似于数组 有length属性,可以使用方括号 [index] 访问指定索引的值(也可以使用item(index)方法)。但nodelist并不是数组。
// 9  element.firstChild/element.lastChild  返回元素的第一个/最后一个子节点(包含注释节点和文本节点)
// 10  element.parentNode  返回该结点的父节点
// 11  element.previousSibling  返回与当前节点同级的上一个节点(包含注释节点和文本节点)
// 12  element.nextSibling   返回与当前节点同级的下一个节点(包含注释节点和文本节点)
// 13  element.childElementCount :  返回子元素(不包括文本节点以及注释节点)的个数
// 14  element.firstElementChild /lastElementChild 返回第一个/最后一个子元素(不包括文本节点以及注释节点)
// 15  element.previousElementSibling/nextElementSibling  返回前一个/后一个兄弟元素(不包括文本节点以及注释节点)
// 16  element.clientHeight/clientWidth  返回内容的可视高度/宽度(不包括边框,边距或滚动条)
// 17  element.offsetHeight/offsetWidth /offsetLeft/offsetTop 返回元素的高度/宽度/相对于父元素的左偏移/上偏移(包括边框和填充,不包括边距)
// 18  element.scrollHeight/scrollWidth/scrollLeft/scrollTop返回整个元素的高度(包括带滚动条的隐蔽的地方)/宽度/返回当前视图中的实际元素的左边缘和左边缘之间的距离/上边缘的距离
// 19  element.style  设置或返回元素的行内样式属性。  element.style.backgroundColor  注意,与CSS不同,style的属性要去掉横杠,第二个单词首字母要大写
// 20  element.tagName  返回元素的标签名(大写)

常用方法

// 1  element.appendChild(nodeName)   向元素添加新的子节点,作为最后一个子节点,并返回该子节点。如需向 HTML DOM 添加新元素,您首先必须创建该元素 ,然后把它追加到已有的元素上。
// 2  element.getAttribute(para)   返回元素节点的指定属性值。
// 3  element.getAttributeNode(para)   返回指定的属性节点。
// 4  element.getElementsByTagName(para) 返回拥有指定标签名的所有子元素的集合。
// 5  element.hasAttribute(para)  如果元素拥有指定属性,则返回true,否则返回 false。
// 6  element.insertBefore(insertNode,appointedNode)  在指定的已有的子节点之前插入新节点。
// js演示代码:
//         var a=document.getElementById('first_form');
//         var inputList=document.getElementsByTagName('input');
//         var newNode=document.createElement('input');
//         var newNode2=document.createTextNode('天马流星拳');
//         var br=document.createElement('br');
//         newNode.type='radio';
//         newNode.name='gongfu';
//         newNode.value='tmlxq';
//         a.insertBefore(newNode,inputList[2]);
//         a.insertBefore(newNode2,inputList[3]);
//         a.insertBefore(br,inputList[3]);

// 7  element.removeAttribute() 从元素中移除指定属性。
// 8  element.removeChild()   从元素中移除子节点。
// 9  element.replaceChild(newNode,replaceNode)  把指定节点替换为新节点。
// 10  element.setAttribute(attrName,attrValue)  把指定属性设置或更改为指定值。
// 11  element.setAttributeNode()    修改指定属性节点
// 12  nodelist.item(0) 返回 NodeList 中位于指定下标的节点。 	nodelist[0]

浏览器对象(window,bom)

W3c 里有,(点击这里)[https://www.w3school.com.cn/jsref/dom_obj_window.asp]

浏览器对象的常用属性

  1. document 文档
  2. history 历史记录: go back forward
    document.querySelector('.back').onclick = function () {
        // 后退两个页面
        history.go(-2)
        // 后退一个页面 = history.go(-1)
        history.back() 
        // 前进一个页面 = history.go(1), go(2)也行
        history.forward()
    }
    
  3. location 当前 URL (页面地址)的信息
  • href 获取地址栏的完整地址
  • hostname 域名
  • pathname 路径
  • search 查询部分
  • protocol 协议
  • hash 锚点链接
    document.querySelector('button').onclick = function () {
        // 实现页面跳转,覆盖掉当前页面
        location.href = 'https://www.baidu.com'
        // 直接 log
        console.log(location.href)
        // 实现页面跳转,打开新的一页
        window.open('https://www.baidu.com')
        }
    
  1. screen 对象包含有关客户端显示屏幕的信息。
  2. navigator 浏览器的相关信息(底层内核的)

算法

冒泡排序

第一轮:1和2比,2和3比,依次类推,把最大的放最后面,
第二轮:1和2比,2和3比,比到倒数第二个数,把第二大的放后面。

var arr = [89, 12, 3, 56, 23, 1, 5]
for (var i = 0; i < arr.length - 1; i++) {
    for (var j = 0; j < arr.length - 1 - i; j++) {
        if (arr[j] > arr[j + 1]) {
            var num = arr[j]
            arr[j] = arr[j + 1]
            arr[j + 1] = num
        }
    }
}
console.log(arr) // 从小到大排序

数组去重

// 1. 简单的方法:includes底层也是for循环,所以3方法更好
var arr = [1, 2, 56, 2, 1, 41, 1, 1, 23, 1, 12, 12, 1, 21, 12123]
var newArr = []
for (var i = 0; i < arr.length; i++) {
    if (!newArr.includes(arr[i])) {
    newArr.push(arr[i])
    }
}
console.log(newArr) 

// 2. 循环去重
for (var i = 0; i < arr.length - 1; i++) {
    for (var j = 1 + i; j < arr.length; j++) {
        if (arr[i] === arr[j]) {
            arr.splice(j, 1)
            j--
        }
    }
}
console.log(arr)

// 3. 利用对象属性名不能重复的特性去重
function distinct(arr) {
    var result = []
    var obj = {}
    for (var i of arr) {
        if (!obj[i]) {
            obj[i] = 1
            result.push(i)
        }
    }
    return result
}
console.log(distinct(arr))

this 指向

// 函数 this     :调用该函数的对象
function fun() {
    console.log(this)  // window
}
window.fun()
document.querySelector('button').onclick = function () {
    console.log(this) // button
}
var obj = {
    name: 'lucy',
    say: function(){
        console.log(this) //obj
    }
}
obj.say()

改变 this 指向: call apply 、 bind

// 当 obj 调用 say 方法时输出的名字是 xiaohong
var obj = {
    name: 'lily',
    age: 19,
    say: function (num) {
        console.log(num)
        console.log(this.name)
    }
}
var obj1 = {
    name: 'xiaohong',
    age: 18
}

// 1. bind 把一个函数改造成一个新的函数并且将原来的函数内的 this 改变成其他对象
var newFun = obj.say.bind(obj1)
console.log(newFun) // 其实就是 say 函数
newFun(5) // 5 xiaohong,

// 2. call 在函数调用的时候将函数内的 this 指向修改。参数直接写在后面
obj.say.call(obj1, 100) // 100 xiaohong
// 3. apply 在函数调用的时候将函数内的 this 指向修改。参数写成数组形式
obj.say.apply(obj1, [9]) // 100 xiaohong

通过 apply 实现查找最大数

var arr = [1, 13, 141, 11, 4, 64, 78, 3]
// 获取数组中的最大数max,min
// Math.max(1, 13, 141, 11, 4, 64, 78, 3)
var maxNum = Math.max.apply(null, arr)
console.log(maxNum) // 141

函数

  • 回调函数:一个函数被当做参数传递给了另外一个函数
    // jquery 的动画回调
    $('button').click(function () {
        // 同步阻塞: 两个同时执行,所以会阻塞
        $('.box').slideDown(1000);
        $('.box1').slideUp(1000);
        // 异步非阻塞(setInterval  setTimeout  jquery动画):box先执行,box1再执行
        $('.box').slideDown(1000, function () {
            $('.box1').slideUp(1000);
        });
    })
    
    // 这是一个简单的回调函数
    function fun1(a) {
        console.log(a)
    }
    function fun2(fun) {
        console.log('先干 fun2 自己的事')
        setTimeout(function () {
            fun(100)
        }, 1000);
    }
    fun2(fun1)
    
  • 递归函数:函数内调用函数本身的函数
    var res = 0
    function fun(a) {
        if (a > 0) {
            res = a + res
            a--
            fun(a)
        }
    }
    fun(100)
    console.log(res)  // 1-100 的和:5050
    
  • 闭包: 一个函数有权访问另一个函数内的变量, 函数内嵌套函数(内层函数永远可以访问外层函数定义的变量)
    // 函数内定义的局部变量当函数执行完毕时就被销毁了,闭包情况除外
    // 例子1:
    function fun() {
        var num = 10
        function fn() {
            console.log(num) 
            // 可以访问到 num,这种情况称为闭包
        }
        fn()
    }
    fun()
    // 例子2:
    function fun() {
        var num = 10
        function fn() {
            console.log(num) 
        }
        return fn
    }
    var f = fun()
    f()
    // 函数内嵌套函数,也能拿到 num(闭包)
    // 类似下面,但下面不属于闭包,也拿不到num
    var f = function(){
        console.log(num) 
    }
    
    // 闭包会将外层作用域内定义的变量一直存储在内存中,等待被访问:比如
    function fun() {
        var num = 10
        function fn() {
            num++
            console.log(num) 
        }
        return fn
    }
    var f = fun()
    f() // 11
    f() // 12
    
    // 可以用闭包来解决以下问题,liArr[i]能拿到i,后面function里不能拿到当时的i
    var liArr = document.querySelectorAll('li')
    for (i = 0; i < liArr.length; i++){
      liArr[i].onclcik = function(){
        console.log(i)   // 输出 4 4 4 4
      }
    }
    // 解决方法:
    var liArr = document.querySelectorAll('li')
    for (i = 0; i < liArr.length; i++){
        function fun(a) {
            // for循环生成了4个闭包,每个闭包存储了上面的i。或者不传参,var a = i 也行
            liArr[a].onclcik = function(){
                console.log(a)   // 输出 0 1 2 3 
            }
        }
        fun(i)
    }
    
    // 立即执行函数:只执行一次
    // 一般情况下做好 jquery 插件里面不能包含全局变量和全局函数,利用立即执行函数封装作用域
    function fn(){
        console.log('hello')
    }
    fn()
    // 改成立即执行函数
    (function (){
        console.log('hello')
    })()
    
    // 小例子1:长度是 4
    for (var i = 0; i < lis.length; i++) {
        setTimeout(function () {
            console.log(i)
        }, 1000);
    }
    // 1 秒之后输出4个4,因为for是同步,setTimeout是异步,异步在同步之后同时输出
    // 小例子2:用闭包解决此问题
    for (var i = 0; i < lis.length; i++) {
        (function(a){
            setTimeout(function () {
                console.log(a)
            }, 1000);
        })(i)
    }
    // 1 秒之后输出 0 1 2 3
    

js 严格模式,点击这里

  1. 禁止不使用 var 或者 const let声明就创建变量
function fun() {
    "use strict";
    num = 100
}
fun()
console.log(num) 
// 不使用严格模式,num为全局,可以输出100,使用严格模式就会报错
  1. 禁止this关键字指向全局对象
var a = 5
function fun() {
    "use strict";
    console.log(this.a)
}
fun()
// 不使用严格模式,this指向window,使用严格模式,输出 undefined
  1. 禁止删除变量(试不出来,主要记住 delete)
"use strict";
var obj = {
    name: 'Lily',
    age: 18
}
delete obj.age
console.log(obj)
// delete可以用来删除变量,结果为 {name:'Lily'}

深拷贝和浅拷贝(针对对象来说)

  • 浅拷贝: 只拷贝第一层,内层的对象只拷贝引用(修改第一层,原对象不变,修改引用第二层,原对象改变)
    1. Object.assign(obj,obj1…) 对象的合并 将后面的对象全部合并到第一个参数内
      var article = {
      title: 'iphone 12',
      author: {
          name: '小明',
          age: 30,
      }
      var newArticle = Object.assign({}, article)
      newArticle.title = 'iphone 13'
      console.log(article,newArticle)
      // iphone 12    iphone 13
      newArticle.author.name = '小红'
      console.log(article,newArticle)
      // '小红'  '小红'
      
    2. … 展开运算符
      var newArticle = { ...article }
      // 效果同上
      
    3. for in 语句 遍历对象
      var newArticle = {}
      for (var key in article) {
      newArticle[key] = article[key]
      }
      // 效果同上
      
    4. 赋值(老师没列为浅拷贝来讲)
      var newArticle = article
      // 效果是里外都能修改,哪层都没拷贝好!
      
  • 深拷贝
    1. JSON.parse(JSON.stringify())
    // JSON.parse  将 json 字符串转化成对象
    // JSON.stringify(obj)  将对象变成 json 字符串
    var newArticle = JSON.parse(JSON.stringify(article))
    // 修改新数组里层外层,原数组不变
    
    1. 利用递归实现深拷贝 for in
    function deepCopy(obj) {
      var newObj = obj instanceof Array ? [] : {}
      for (var key in obj) {
        if (obj[key] instanceof Object && !obj[key] instanceof Function) {
          newObj[key] = deepCopy(obj[key])
        } else {
          newObj[key] = obj[key]
        }
      }
      return newObj
    }
    deepCopy(article)
    // 可以实现深拷贝
    
    // instanceof    数据 instanceof 对象类型(首字母大写)   结果是 true 或 false
    // 数组  函数  {}  用来检测 Object 都是 true
    var a = function () { }
    console.log(a instanceof Object)
    // true
    

构造函数

// 所有的内置对象 都是由js的内置构造函数创建出来的
// 构造函数(类):用来创建对象的, 函数名首字母必须大写
function Hero(name, age) {
    // this 指向的是函数调用的时候的实例化对象(a)
    this.name = name
    this.age = age
    this.say = function(){
        console.log('我很厉害')
    }
}
var a = new Hero('小乔', 20)
a.say() // 我很厉害

构造函数的原型

// 每一个函数都有一个原型,只有构造函数的原型有意义
// 构造函数的原型对象   函数名.prototype 
// 给 Hero 添加了一个公共的方法 sing
Hero.prototype.sing = function () {
    console.log('我会唱歌')
}
a.sing()   // 我会唱歌
// 为什么能够输出:实例化对象(用构造函数创建出来的对象)有一个属性叫__proto__ ,这个属性指向的就是构造函数的原型对象
// 构造函数的原型对象内默认会有一个 constructor 属性 该属性指向的就是构造函数本身
// 如果构造函数的原型等于一个对象的话,constructor就没有了,如下:
Hero.prototype = {
    sing:function(){
        console.log('我会唱歌')
    },
    dance:function(){
        console.log('我会跳舞')
    }
}

var b = new Hero('大乔', 21)
b.sing = function(){
    console.log('我太会唱歌了')
}
// a.sing 不会变,b.sing 变;因为没改公共的属性

小应用1:给数组添加一个求和方法

Array.prototype.qiuhe = function () {
    // 求和
    var res = 0
    // this 指的是构造函数的原型 arr
    for (const num of this) {
        res += num
    }
    return res
}
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.qiuhe()) // 1-6 的和

小应用2:polyfill 处理兼容 有些内置对象的方法比较高级 低版本浏览器不支持

// Object.defineProperty(obj,属性名,属性值)
// Array.prototype.includes = function () { 自己做判断 }
// 上面和下面基本一致(区别不用懂)
Object.defineProperty(Array.prototype, 'includes', function () { 自己做判断 })

小应用2:高亮插件里的小知识

// $('.box')  创建出来了一个 jquery 的实例化对象 
$.prototype.hello = function () {
    console.log('hello jquery')
}
$('.box').hello()
// hello jquery

// jquery 提倡使用  jQuery.fn.extend 添加 jquery 的公共功能
jQuery.fn.extend({
  highLight: function (setting) {
    // 后面俩对象合并到空对象中赋值给options
    var options = $.extend({}, jQuery.fn.highLight.defaultSetting, setting)
    this.css({ 'color': options.color, 'background-color': options.bgColor })
  }
})
// 避免创建全局变量(这是默认的样式,函数也有方法,一般大佬都这样写)
jQuery.fn.highLight.defaultSetting = {
  color: 'red',
  bgColor: '#ccc'
}

// 引用:用户可以在页面上设置自己的默认样式(上面的是插件)
$('.x').highLight() 
// 直接是默认样式
$('.x:eq(2)').highLight({ color: 'pink' })
// 这种颜色是粉色,背景色是灰色

小知识:对象的合并

  1. Object.assign({} , obj ,obj1) 浅拷贝中有介绍
  2. $.extend({},obj, obj1) 后面的对象和前面对象如果出现重名属性 后面的覆盖前面的

复杂数据 object对象下的方法

  1. 修改或添加属性 Object.defineProperties(obj,props)
var goods = {
    id: 1,
    name: 'iphone12'
}
Object.defineProperties(goods, {
    price: {
        value: 10000,
        // 是否可以重写  默认是 false
        writable: true
    },
    name: {
        value: 'iphone 12',
        writable: true
    }
})
goods.price = 12000
// id: 1,  name: 'iphone12', price: 12000
  1. 获取对象属性名的集合 Object.keys(obj)
console.log(Object.keys(goods))
// [id,name,price]
基本 shell 命令

需要使用命令行,安装 git bash 即可。

ls 命令

查看当前目录下的所有内容

命令参数

  • -a 查看当前目录下的所有内容包括隐藏文件
cd 命令

跳转目录
特殊的目录

  • ~ 用户主目录,存储着很多默认配置
mkdir 命令

创建文件夹

touch 命令

创建文件,需要加后缀名

rm 命令

删除文件或文件夹

参数

  • -r 删除文件夹
  • -f 强制删除
pwd 命令

打印当前位置

cp 命令

复制或重命名
参数

  • -r 可以复制文件夹
mv 命令

剪切

cat 命令

查看文件里面的内容

安装注意

不需要选择安装目录,一直下一步,直到出现很多复选框其中有两个是 git bash heregit gui here 将后面的勾选掉(不勾选),继续一直下一步即可。安装完毕之后在任意空白处点击鼠标右键就会出现 git bash here 命令。

git 的基本操作
本地的项目已经做好了(做了一部分,项目刚开始),向要传到 github 网站保存
上传到网上非空仓库 (比如 [username].github.io)
  • 先将网上的仓库下载到本地,需要使用命令 git clone 仓库地址
  • 将想要托管的项目放到该仓库内,仓库内不必要的东西删除
  • 将本地和网上关联起来(将自己本地的修改上传)
    • 将自己对仓库的所有修改添加到 github 远端记录,执行 git add .
    • 将记录好的修改做成版本,执行命令 git commit -m'留言'
      • 如果是第一次和 github 关联的话,会失败,然后会提示
          please tell me who you are ?
          让你依次分别执行以下命令
          git config --global user.name "yourname"
          git config --global user.email "youremail"
        
      • 告诉他之后再次执行 git commit -m'留言'
    • 将新的版本上传,执行命名 git push
      • 由于是第一次和网上关联,会失败提示
          让你执行
          git push -u origin master
          执行完毕之后就成功了
        
  • 上传成功之后,想要修改的话,直接在本地仓库修改然后执行上传三部曲即可
上传到网上空仓库 (我创建的 first_demo 空仓库)
  • 网上传建一个项目同名(你的项目文件夹是什么名字,仓库名与之相同)空仓库(创建的时候不添加 reademe 文件)
  • 在你的本地项目文件夹内打开命令行工具
  • 将自己本地的项目初始化成 git 仓库,执行 git init 命令
  • 执行 add 和 commit 命令(简写的需要自己补充)
  • 去网上你的空仓库复制代码,第一个提示框的最后两行
      git remote add origin https://github.com/Sunny-zz/first_demo.git
      这行命令作用是给本地的仓库添加一个远端地址 名称叫 origin 地址是 后面的地址
      git push -u origin master
      将版本上传到已经添加的远端 origin
    
  • 执行完毕之后上传成功,想要修改的话,直接在本地仓库修改然后执行上传三部曲即可
使用 ssh-key 秘钥

将自己的电脑的当前系统和 github 关联。在上传或下载的时候使用 ssh 方式,省略输入用户民和密码。关联步骤如下

  • 打开命令行工具 输入 cd ~ 命令
  • 执行 ssh-keygen 命令,生成电脑上的公钥和私钥,直接一直回车直到出现密码图即可。
  • 找到生成的公钥和私钥,位置在 ~/.ssh/ 文件夹下,使用 cat 命令打印出公钥内容并复制 cat .ssh/id_rsa.pub
    • 如果生成的秘钥名称不是 id_rsa.pub 的话自己去找一下
    • 执行 cd .ssh 在执行 ls -a 查看自己的秘钥名称
    • 在使用 cat 你的公钥 去复制。
  • 打开 github 网站,点击头像的下箭头选择 setting,找到 ssh key 新增一个即可。
  • 验证 ssh 是否生效的话,需要重新从网上下载(选择 ssh 方式下载)仓库到本地,再次上传更新的时候就不需要输入用户名和密码了。
多个人或者多台电脑对同一个仓库的同一个分支进行操作
  • 同事 a 对 first_demo 仓库进行了修改,在 index.html 内添加了 一个轮播图,上传提交成功

  • 同事 b 被要求添加一个 about 页面在项目内,做完之后上传执行 git 上传三部曲。执行 git push 失败提示远端存在本地不存在的版本,可以使用 git pull 命令。

  • 执行 git pull 将远端的版本拉取到本地,但是本地已经存在了一个为提交的版本,

    当这两个版本不冲突的时候(不时同一个文件,或者同一个文件的不同位置),

    • git 会自动合并两个版本,并弹出新的命令面板让你提交合并的版本留言,可以直接按住 shift 并按两下 z,退出留言面板。
    • 再次执行 git push,上传更新

    当这两个版本冲突的时候(修改的是同一个文件的同一个位置)

    • git 并不会自动合并,而是将冲突体现在当前的文件内,需要用户自己选择保留哪个更改,选择完毕之后,使用 git 上传三步提交。
github 的分支操作

一个仓库内可以有多个分支,默认只有一个分支 master ,通常称为主分支(用来放合并后的代码)。也可以创建无数个其他分支。一般工作流程是先在主分支将项目的主体框架搭建完毕,然后创建多个分支,每个分支代表不同的功能,不同的程序猿分别在不同分支内进行开发,开发完毕将代码合并到主分支。

  • 创建一个仓库(breach_demo)带上 readMe ,然后克隆到本地,添加上公共的 css 和 js 然后上传。
  • 创建新的分支 a 和 b,使用 git branch agit branch b 命令。分支创建的时候里面的内容和主分支是一模一样的。
  • 需要将创建好的分支上传到网上,可能直接使用 git push 就会成功,但是由于网上并没有新分支,所以需要使用 git push --set-upstream origin yourbranch 上传。
  • 现在本地和网上同步(三个分支都同步了),接下来就可以分开工作了。
  • 分别切换到 a 分支和 b 分支,展开自己的工作,创建新的页面并上传。
  • 主分支分别合并两个分支的内容,然后上传。需要使用 git merge yourbranch 命令合并分支。合并之后上传。
github 的特殊分支 gh-pages

拿我们创建好的 branch_demo 仓库来说,master 分支内已经存放好了其他分支做好的项目(项目成品,包括 index…),github 仓库有一个特殊的分支叫 gh-pages ,该分支下的内容会被自动托管到 github 免费服务器(也就是说只要该分支下有 index.html 就可以直接使用网址访问)
只需要直接创建 gh-pages 分支,并上传到网上即可。

仓库根目录下的 .gitignore 文件

gitignore 文件是一个隐藏文件,该文件的作用是当你将一些文件或文件夹的名称写在 .gitignore 内的话,该仓库执行上传操作的时候,会忽略 .gitignore 内添加的文件或文件夹

git 常用命令
  • git --version 查看 git 版本号,有时候可以简写成 -v
  • git clone 仓库地址 克隆仓库到本地
  • git add . 将你的修改让远端记录 . 代表所有的修改 也可以换成文件名
  • git commit -m'留言' 将记录好的修改做成版本,并提交版本留言
  • git push 将做好的版本提交到远端
  • git init 将本地项目变成仓库
  • git status 查看当前仓库的状态
  • git log 查看本地版本
  • git pull 将远端的更新拉取到本地
  • git branch newBranch 创建新分支
  • git branch 查看分支
  • git checkout yourbranch 切换分支
  • git merge yourbranch 当前分支合并分支其他分支
  • git pull origin master 拉取主分支上的更新
  • git checkout -b newbranch 创建新的分支并切换过去
注意
  • 仓库不能嵌套仓库
  • 版本回退
类似 github 网站的其他工具
  • 腾讯云开发者平台,是一个和 github 类似的网站,服务器是国内的操作起来很快。
  1. 微信扫码登录
  2. 项目->创建项目->DevOps项目->直接创建->打开项目->新建代码仓库->
  • 码云 一般从 github 下载项目,可以通过 码云 下载
ECMAScript 6 es2015 新 js(ES6)
const 和 let

let const 和 var 的不同

const 是声明常量(值不可修改,例如 π)的,常量的名称都是全大写的(单对于const)

  • 不能重复声明(同一个变量声明两次就报错,而var第二个覆盖第一个)
  • 没有声明提升(先console,在赋值,var会显示undefined。但是let,const就错误提示)
    console.log(num);
    var num = 10
    // 如果没有定义num,直接log,就提示 num is not defined
    
  • 存在块级作用域(作用域被定义在 {} 内)
    for (let index = 0; index < 10; index++) {
    }
    console.log(index)
    // var:11
    // let: index is not defined
    // const:出错,因为不能修改const定义的数
    
    

let 声明的变量是可以被修改的

变量的解构赋值

对象解构赋值

const obj = {
  username: '貂蝉貂蝉',
  userage: 18,
  level: 10,
}
const { username, userage: age } = obj
console.log(username, age)
// 貂蝉貂蝉,   18

字符串的解构赋值

const str = 'hello vue'
const [d, e, f] = str
console.log(d, e, f)
// h e l

数组的解构赋值

const arr = [1, 2, 3, 4, 5]
const [a, b, c] = arr
console.log(a, b, c)
// 1 2 3

函数参数的解构赋值

const obj = {
  username: '貂蝉',
  userage: 18,
  level: 10,
}
function showInfo({ username, level }) {
  // const { username, level } = obj
  console.log(`该英雄的名称是${username}`, `等级${level}`)
}
showInfo(obj)

技巧: 实现变量调换

let x = 1
let y = 2

[x, y] = [y, x]

const 的小应用

const obj = {
  name: '吕布',
  age: 28,
}
obj.level = 15
// 这样能加进去,因为const obj是地址,而改的是地址里面的东西,地址没变
const obj = {
  name: '吕布',
  age: 28
}
obj = {
  name: '吕布',
  age: 28,
  level=18
}
//这样就会报错,因为地址已经改了。
字符串的扩展

模版字符串

const username = 'lucy'
console.log(`my name is ${username}`)

新增的字符串方法
includes(), startsWith(), endsWith(), trimStart(),trimEnd(),padStart(),padEnd(),matchAll()

函数的扩展

函数参数的默认值
普通方式参数非对象

// 函数参数的默认值,默认有值
const fun = function (color = '黑色', bgColor = '红色') {
  console.log('颜色:::', color)
  console.log('背景色:::', bgColor)
  // 蓝色   红色
}
fun('蓝色')

参数为对象

const fun = function ({ color = '黑色', bgColor = '蓝色' }) {
  console.log('颜色:::', color)
  console.log('背景色:::', bgColor)
  //  粉色   蓝色
}
fun({ color: '粉色' })
// 不能什么都不传,最起码传递一个空对象

rest(剩余) 参数

function add(a, b, ...rest) {
  // 用 ... 后面加变量,指的就是剩余的参数
  console.log(a, b, rest)
  // 1  2   [3, 4, 5, 6, 7, 8, 9, 10]
  // rest 是数组
}
add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

箭头函数写法(省略了function)


  • 箭头函数定义只能变量式定义,
  • 箭头左边是 函数的参数部分 使用小括号包裹参数逗号拼接,当参数只有一个的时候可以省略小括号
    const add = num1 => num1
    
  • 箭头右边是 函数主体,使用花括号包裹,返回值设置依然使用 return。当函数不需要操作就设置返回值的话可以省略花括号和 return 直接写返回值即可
    // 1.
    const add = (num1, num2) => {
      return num1 + num2
    }
    // 2.
    const add = (num1, num2) => num1 + num2
    // 调用
    const res = add(10, 20)
    console.log(res)
    // 30
    

箭头函数和普通函数的区别

  • 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
var username = 'window'
function xx() {
  var username = 'local'
  console.log(this.username)
  // window ,因为是window调用的xx,谁调用的,this指谁
}
xx()

const obj = {
  name: 'lily',
  say: () => {
    console.log(this.name)
    // 是空的,因为箭头函数里的this指得使用时的组件(window)
  },
}
obj.say()

const obj = {
  name: 'lily',
  say: function () {
    console.log(this.name)
    // 显示lily,因为调用的是obj,普通函数里this指的是当前组件obj
  },
}
obj.say()
  • 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
  • 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
数组的扩展

Array.from(): 将类数组转化为数组

const fun1 = function () {
  // arguments算是一个类数组
  console.log(arguments)
  // [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
  console.log(Array.from(arguments))
  // [1, 2, 3, 4]
}
fun1(1, 2, 3, 4)

// 类数组和对象类似,大概如下:
const obj = {
  // 前面属于下标,必须按顺序写,不然就undefined
  '0': 12312,
  '1': 98768,
  // 这是长度,必须写对,否则多出来的undefined
  length: 2,
  // 这写随便其它的变量
}
console.log(Array.from(obj))
// [12312, 98768]

Array.of(): 将几个数变成数组

  const num1 = 11
  const num2 = 111
  const num3 = 111
  const num4 = 112
  const arr = Array.of(num1, num2, num3, num4)
  console.log(arr);
  // [11, 111, 111, 112]

数组新增方法 flat,flatMap

扩展运算符

作用是对象的拷贝,还有类数组转化数组

  • 对象展开
  • 数组展开
const obj = {
  name: '庄周',
  age: 18,
}
const obj1 = { ...obj }
obj1.hobby = '浪'
console.log(obj, obj1)

const arr = [1, 2, 3]
const arr1 = [...arr]
arr1.push(4)
console.log(arr, arr1)
对象的扩展

对象的简洁表示法

const username = '哈哈'
const userage = 20
const obj = {
  username,
  // 当对象的属性名和作为该属性的属性值的变量名相同时
  userage,
  // 函数可以省略 function,是普通函数
  say() {},
}
console.log(obj)
Symbol

第七种数据类型,生成独一无二的数据

set 数据结构

类似于数组,但是不能存重复的值

const ary = new Set([1, 2, 131, 312, 1, 2, 131])
console.log(ary)
// 属性
// size
console.log(ary.size)
// 方法
// add() 向set数据内添加一个成员,返回数据本身
ary.add(1000)
console.log(ary)
// delete() 删除某个值,返回一个布尔值
//  has() 查看该值是否为Set的成员,返回一个布尔值
// clear() 清除所有成员
// 如何将 set 数据转化成数组
console.log([...ary])

Set 结构的实例有四个遍历方法,可以用于遍历成员。

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

还有一个额外的 WeakSet 数据结构,内部成员只能是对象类型

class 类

写法

class Hero {
  // 类的花括号内默认一般只写方法,而且方法之间不需要逗号
  // constructor 是 class 自带函数,该函数被称作构造器和以前的构造函数类似
  // constructor 函数当 创建实例化类的时候自动触发
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  // 除了 constructor 函数之外定义的函数都相当于原来的 prototype 内的方法
  say = () => {
    console.log('我是王者荣耀的英雄' + this.name)
  }
}
const a = new Hero('牛', 20)
const b = new Hero('小乔', 18)
console.log(a)
console.log(b)
a.say()

继承

class CarryHero extends Hero {
  constructor(name, age) {
    super(name, age)
    // super 调用了才真正实现了继承
  }
}
const c = new CarryHero('赵云', 19)
console.log(c)
node

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。
安装
直接百度 node 进入中文网直接下载安装包,直接进行安装不需要做任何的选择,一直下一步即可。
任意位置打开命令行工具,输入 node -vnpm -v 查看 node 和 npm 的版本号,如果有就安装好了,如果命令出错尝试重启电脑再次执行。

npm 是什么
安装好 node 之后 npm 就附带安装好了。他是 node package manager,node 包管理工具。

node 模块(后台,前端)

前端模块包括前端所有的 js 相关的包。

node 模块使用

  • 新建文件夹,将该文件夹初始化为 node(npm) 的项目执行 npm init -y
  • 新建 index.html 页面
  • 安装 jquery 插件。执行 npm install jquery
  • 安装完毕之后,项目内多了点东西
    • node_modules 文件夹,该文件夹是存储下载的包的地方
    • package-lock.json 文件,该文件是记录安装的包的详细信息的,不需要理会
  • 在 html 中使用 jquery 可以按照之前的路径导入(不推荐)。但是现在是使用 npm 下载模块。可以使用 node 模块的方式导入安装好的包。 使用 require(包名)
  • 但是当做模块导入之后浏览器报错。因为模块语法浏览器不支持需要编译。

npm 的使用
npm 就是下载 node 包的工具。
下载方式有三种

  • npm i 包名 --save,这种方式一般下载的是项目的必须依赖,记录到 package.json 内的 dependencies 字段内。
      npm install jquery@2.3.4 --save
      npm i jquery -S
      npm i jquery
    
  • npm i 包名 包名 --save-dev,这种方式安装的是项目的非必须依赖(工具类),记录到 package.json 内的 devDependencies 字段内
      npm install webpack --save-dev
      npm i webpack -D
    
  • npm i -g 包名,这种方式是全局安装,当你想要在你的电脑上任何地方都使用包的时候进行全局安装。
      npm i -g server
    

卸载包使用 npm uninstall 包通过哪种方式安装的就怎样卸载。

npm 下载包的好处

  • 可以使用模块导入
  • 下载的包的信息全部记录在 package.json 内
  • 同事之间相互传递项目的时候不需要传递 node_modules 文件夹。使用的时候只需要执行 npm i 命令会重新将所有的包下载一遍。

npm 的技巧,直接在任意位置的命令行执行即可

  • 切换 npm 包的来源,默认的来源是外网服务器有点慢

      npm config set registry https://registry.npm.taobao.org
    
  • 加上 http 显示配置,不让等待过程显得无聊。

      npm config set loglevel=http
    

提示:如果想在命令行里查看js浏览器中不支持的require,用 $ node 路径($ node ./js/…),要想让浏览器认识,可以通过webpack编译

node 模块语法
  • 前端模块分类 :核心模块 第三方模块 自定义模块
  • 模块导入: require(‘包名’) 前两种模块直接写包名 自定义模块需要写路径
  • 模块导出: module.exports={值}
    (提示:没写{}默认导出第一个值,写了{},那么两个都有或者 a:66)
使用 webpack 打包编译我们的项目

你的 node 项目内使用 node 模块导入各种依赖,webpack 可以实现将模块的导入导出编译成浏览器认识的语法,也可以将所有的导入模块操作打包。
如何使用 参考链接

  • 项目内安装 webpack
      npm install webpack webpack-cli  --save-dev
    
  • 将 js 文件夹的名字改成 src,保证项目的根目录有 src ,并且 src 下存在 index.js,还有 index.js 是页面的主要用的 js
  • 执行编译打包命令 npx webpack,会将 index.js 打包编译到项目下的 dist 文件夹下的 main.js
  • 页面导入打包好的 js
  • 上面是使用了 webpack 的默认配置进行的打包,可以在项目根目录下新建 webpack.config.js 文件,当作 webpack 编译的配置文件。参考网址 https://www.webpackjs.com/guides/getting-started/#%E4%BD%BF%E7%94%A8%E4%B8%80%E4%B8%AA%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6,复制基础的配置到该文件内。
  • 一直敲编译命令很繁琐,可以使用 package.json 中的 scripts 字段配置’快捷键’。使用 npm run 名 快捷执行。
    (提示:在package.json下的scripts里输入的 "build": "npx webpack --config webpack.confi.js")
module

es6导入两种方式

  • 默认导入
  • 命名导入

导出两种种方式

  • 默认导出
  • 命名导出
es6 模块写法

// 模块路径 第三方和核心模块直接写包名

  • 默认导入:import x from "./index.js"
  • 默认导出: export default a;
  • 默认导入和默认导出的名字可以不一样(x,a),默认导出只能导一次,也只能导一个变量
  //默认导入
  import x from "./index.js"
  import $ from "jquery"
  //默认导出a
  export default a
  • 命名导入:import xxx, { a as x, b } from "./about";
    import * as obj from "./about";
  • 命名导出:export const a = 100;
  • 命名导入名字必须和导出一致,可以使用 as 换名
  • 命名导出和默认导出可以同时存在,命名导出可以使用多次
  • 全部导入不管是默认的还是命名的 obj 内存在所有的导出
  • xxx 代表默认的,{}内代表命名的
ajax

Asynchronous JavaScript and XML(异步的 JavaScript 和 XML).
在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
现在已经进阶了,进阶成 Asynchronous JavaScript and JSON。

原生 ajax
  • 创建 xhr 对象
  • 使用对象创建请求
  • 使用 send 发出请求
  • 使用 onreadystatechange 函数监听请求的过程,获取响应
// get
const xhr = new XMLHttpRequest();
// 创建请求  open('请求的类型','地址','是否异步')
// 请求的类型 后台规定的
// GET  POST  PUT  PATCH DELETE ...
xhr.open("GET", "http://jsonplaceholder.typicode.com/posts", true);
xhr.send();
// 监听整个请求过程
// xhr.readyState 请求状态 0-4    4请求成功响应就绪
// xhr.status 请求状态码  200 ok
// xhr.responseText 返回的数据
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    // 可以获取后台数据
    console.log(JSON.parse(xhr.responseText));
    // 获取到的数据类似数组字符串   ---> json 串
    // 属性名和属性值必须使用双引号,数字和布尔值不需要,最后一项没有逗号
    // 使用 JSON.parse(json串) 转换
  }
};
// post 请求 类似登录
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://cnodejs.org/api/v1/accesstoken", true);
// 发送请求的时候需要传递给后台数据
// 但是原生 ajax 不能接收对象为参数,只能接收 json 串,而且得设置请求可以传递 json
// 需要使用 xhr.setRequestHeader() 设置请求头
// 添加 json 为可传递数据,使用 JSON.stringify 将对象转化为 json 串
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(
  JSON.stringify({ accesstoken: "ecf878d1-6052-476a-8262-824760c7872b" })
);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};
jquery ajax
  • get 请求
  • $.get(地址,参数,回调函数,返回内容的格式)
  • 地址 后台接口地址
  • 参数 请求的参数,不需要可省略
  • 回调函数 成功之后的回调函数默认接收一个参数就是返回的结果
  • 返回内容的格式 默认带 json 不需要设置
$.get("http://jsonplaceholder.typicode.com/posts", function (res) {
  console.log(res)
  });
  • post 请求
  • $.post(地址,参数,回调函数,返回内容的格式)
  • 参数的意思和 get 一致
  $.post(
        "https://cnodejs.org/api/v1/accesstoken",
        { accesstoken: "ecf878d1-6052-476a-8262-824760c7872b" },
        function (res) {
          console.log(res);
        }
   );

常用方法 $.ajax({配置对象})

// get(默认 type 是 get,不区分大小写)
$.ajax({
  type: "get",
  url:"http://jsonplaceholder.typicode.com/posts",
  success: function (res) {
    console.log(res);
  },
  // 请求失败的回调
  error: function (err) {
    console.log(err);
  },
})

//post
$.ajax({
  // 请求类型
  type: "POST",
  // 请求地址
  url: "https://cnodejs.org/api/v1/accesstoken",
  // 请求参数
  data: { accesstoken: "ecf878d1-6052-476a-8262-824760c7872" },
  // 请求成功的回调
  success: function (res) {
    console.log(res);
  },
  // 请求失败的回调
  error: function (err) {
    console.log(err);
  },
  // 请求结束的回调,无论是成功还是失败
  complete: function () {
    console.log("完事")
  },
  // 发给后台的内容类型 默认支持 对象类型
  // contentType: ""
  // 请求是否同域 false 代表同域请求  true 代表跨域请求
  // 一般来说后台独立解决了跨域请求的问题,不需要前端进行配置
  // 也有前后端一起解决跨域问题,后台需要前端做一些简单的配置
  // crossDomain: 布尔值
  // headers: 请求头的设置  可能需要配合后台做一些设置
});

请求的地址:http://localhost:3008/posts?id=1(自己建的)

// 地址栏的问号部分就是查询部分
$.ajax({
  // url: "http://localhost:3008/posts?_limit=10&_page=1",
  //上面url和下面的两个是一样的,可以把?后面的查询部分写在data里
  url: "http://localhost:3008/posts",
  data: { _limit: 10, _page: 1 },
  success(res) {
    console.log(res);
    //结果拿到的是对象,后面加上.data就变成数组了
  }
});
axios ajax

axios 是专门的 ajax 请求插件,它里面的异步解决方案使用的是 promise。文档参考网址,axios的有点复杂的介绍网址

  • 异步非阻塞
  • 同步阻塞
// get
axios
  .get("http://localhost:3008/users", {
    params: {
      _limit: 20, _page: 2
    },
  })
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.log(error);
  });
// axios 就是使用 promise 封装了异步操作
// axios.get('地址').then(res=>{}).catch(err=>{})
// then 是成功函数  res 是成功的返回值  axios 会将后台的数据存储到一个对象的data属性内


// post
axios
  .post("https://cnodejs.org/api/v1/accesstoken", {
    accesstoken: "ecf878d1-6052-476a-8262-824760c7872b",
  })
  .then(res => {
    console.log(res.data);
  });

异步同步的小例子

setTimeout(() => {
    // 异步完毕之后执行的事
    console.log("异步完毕");
  }, 1000);
  console.log(1);
  // 嵌套很多的回调,一旦某一环节需要做额外的判断处理
  // 回调地狱
  $(".box").slideDown(1000, function () {
      $('.bxo')
  });

promise(把异步操作放在promise里的小例子)

//第一种简单的小例子
let num = 10;
console.log("正在干活计数");
const promise = new Promise(function (resolve, reject) {
  // resolve 函数的意思就是成功的回调函数
  // reject 函数的意思就是失败的回调函数
  setTimeout(() => {
    num = 100;
    if (num === 100) {
      console.log("活干完了");
      resolve();
    } else {
      console.log("有点问题没做完");
      reject();
    }
  }, 1000);
});
// .then 就是传递 resolve 函数
// .catch 就是传递 reject 函数
// 避免了回调嵌套
promise.then(function () {
  console.log("计数完成了,进行下一步操作");
});
promise.catch(function () {
  console.log("没干完,得继续干");
});

//第二种一般这样使用的小例子
function timeout(ms) {
  return new Promise((resolve, reject) => {
    console.log("异步操作正在执行");
    setTimeout(resolve, ms, "done");
  });
}

timeout(1000).then(value => {
  console.log("执行完毕之后做的事");
  console.log(value);
  //value的值是done,详解如下一个js
});
setTimeout(
  (value, value1) => {
    console.log(value, value1);
  },
  1000,
  1,
  2
);
// 输出1 2,具体为什么自己想,hahaha 
使用 json-server 模拟后台数据库
  • 全局安装 json-server npm i -g json-server 可能有点慢,安装一次就够了
  • 在需要的地方新建 xxx.json 文件,还文件内写法如下
    {
      "users": [
        {
          "id": 121,
          "username": "小王"
        },
        {
          "id": 21,
          "username": "小二"
        }
      ]
    }
    
  • 这样的 json 文件生成的数据代表 users 列表数据
  • 数据列表中最后不能加逗号,可以写多个列表数据(接口)
  • 接口文档的值必须是对象或数组(users是一个接口,可以有多个接口)
  • 在该 json 文件所在的地方打开命令行工具,启动数据库服务。json-server --watch xxx.json -p 3000,命令执行完毕之后,数据库就启动了,不要关闭该服务。安装上述 json 文件,启动的服务可以使用 ‘http://localhost:3008/users’ 接口就能访问用户列表了。
  • 更详细的接口文档说明参考json-server(就是去掉接口名)
将自己的项目跑在本地服务器上
  • 全局安装 serve npm i -g serve,安装一次就够了
  • 在你的项目内打开命令行工具执行 serve .
  • 执行完毕之后你的当前项目内的所有文件已经被跑在了 http://localhost:5000 服务器上,默认打开 index.html
项目服务器

项目的开发阶段,前端页面跑在本地(局域网)服务器上,后台数据库服务器只能公司内部访问。

vue 框架

vue安装

想要使用 vue 开发项目,需要安装 vue 的开发环境。官方网站提供了安装环境的脚手架工具 vue-cli。使用命令 npm install -g @vue/cli 全局安装脚手架工具。工具安装好之后就可以搭建 vue 的开发环境了。有两种方式搭建

  • vue create 命令,在想要创建项目的文件夹下执行 vue create 项目名
  • 图形化界面工具。任意位置执行 vue ui 在浏览器中调出图形化界面,创建 vue 项目。
    • 选择上方的创建按钮
    • 选好项目所要创建的位置
    • 选择默认的预设模版
    • 创建
    • 创建完成之后,选择 任务–>serve–>运行–>启动 app
    • 运行还可以使用命令:npm run serve
    • 此时 vue 的基础项目已经搭建完毕。

vue 初始项目结构

  • node_modules 该项目依赖包的存放位置
  • .gitignore 作为 github 仓库忽略上传的记录
  • package.json 记录了 node 项目的基础配置,和一些 vue 项目的配置
  • package-lock.json 详细记录项目所用到的包
  • README.md 项目介绍文件
  • public 该文件下存在的是项目的 html 模版,意思就是你写的所有的 vue 代码其实都是以该模版为基础的,一般不需要修改,偶尔可能会引入一些文件(css js 等工具类的)
  • babel.config.js babel 的配置文件,babel 是一个 js 编译工具,作用是编译新版本的 js 语法。
  • src 文件夹是 vue 项目的源代码
    • assets 存储静态文件的文件夹,一般存放一些 公共的 css 图片等
    • components 存放 vue 组件的,文件夹名称可以修改
    • app.vue 该文件就是 vue 的最外层组件
    • main.js 该项目的入口文件,意思是 webpack 会将该文件打包编译。该文件内写的是 vue 一些全局设置。

spa 单页面应用

vue 项目创建的就是单页面应用。整个项目就在一个页面内。参考文档单页面

vue 组件

整个 vue 项目就是由各种各样的组件组合而成的。组件可以理解成我们原来排版的某一个结构部分。app.vue 组件是项目的最外层结构。在 vue 项目中,最简单的组件写法就是以 vue 为后缀名的文件,组件名称一般首字母大写,多个字母使用大驼峰方式命名。
如何划分组件,其实就是和之前画盒子一样。
vue 后缀的组件构成

  • html 部分,使用 template 标签表示,里面写 html 即可,也可以嵌入其他的组件,需要注意的就是该 template 标签只能有一个子级
  • script 组件的 js 部分。自己页面的逻辑处理,其他组件的注册。
  • style 组件的 css 部分,默认的样式是全局的。

components 的作用

main.js 的作用

import Vue from "vue";
// 导入 Vue
import App from "./App.vue";
// 导入 最大组件 app
import "./assets/style.css";
// 导入全局的 css
Vue.config.productionTip = false;
// Vue 项目的配置去掉生产版本提示
// 现在是开发阶段,看不到打包之后的代码,打包之后的代码被托管到服务器上,所以我们可以通过访问服务器地址访问我们的项目
new Vue({
  render: h => h(App)
}).$mount("#app");
// 创建 vue 实例 添加render配置,作用是需要渲染的组件
// 实例创建好之后使用实例的 $mount 方法将实例挂载到页面的 #app 结构内

组件样式

组件的样式基本上都是全局的,因为只有一个页面,所有的组件都会渲染到同一个页面。

  • 在 style 标签上加上 scoped 属性,使组件样式私有化
  • 在 style 标签上加上 lang 属性,可以设置使用高级 css 扩展语法(less sass)

组件的嵌套

  1. 先在父组件内导入子组件
  2. 在父组件内的导出对象内使用 components 属性先注册子组件。
<script>
 // 第一步,先在父组件内导入子组件
  import Header from "./components/Header";

  // 在组件内js部分必须默认导出一个对象,而且该对象下一般必须设置一个属性 name 属性值可以和组件名一致,不能和 html 标签命名冲突
  export default {
    name: "App",
    // 第二步 组件注册
    components: {
      // Header: Header,
      // 对应的是在wrap里引用的那个:上面导入的变量
      Header,
    },
  };
</script>

<style scoped>
// scoped 的作用就是给该组件的所有标签加上一个随机属性(和所有组件都不同),所有选择器都添加上了该属性的属性选择器
// 那么加了 scoped 属性的样式就变成了私有样式了 
</style>

  1. 在父组件的 template 内直接使用组件名称的标签即可
<div class="wrap">
  <header />
  <!-- header 名字任意起的 -->
</div>

组件的复用技巧 props

当一个组件需要在很多个组件内使用,而且多多少少显示的内容不一样,其实是需要根据组件所在位置进行轻微的修改。此时就可以借助 vue 内的 props 知识点处理。
父组件嵌套子组件的时候希望子组件要根据我的想法修改一些内容。
props 的使用

  1. 在父组件内,直接当做自定义属性传递即可
<Button value='warning' text="Download Now" color="#00f" :isActive="true" />
  1. 在子组件内,需要使用导出对象下的 props 属性接收
    方式一数组
export default {
  name: "Button",
  // 接收的时候使用字符串  数组方式
  props: ["text", "color"]
};

方式二对象

export default {
  name: "Button",
  // 对象方式 高级可以做简单的校验
  props: {
    text: {
      //  可以设置属性的类型和默认值
      type: String,
      default: "default button"
      // required: true,
      //必填的字符串,和default冲突,两个一起写default生效,但还是会提示红色错误
    },
    isActive: {
      type: Boolean,
      default: false
    },
    value: {
      type: String,
      default: "success",
      // 可以自定制匹配校验
      validator: function (value) {
        // value 代表的就是父组件传递的值
        // 这个值必须匹配下列字符串中的一个,能执行,但也出现红色错误
        return ["success", "warning", "danger"].indexOf(value) !== -1;
      },
    },
  }
};
  1. 接收完毕之后需要在 template 中使用,使用方式分为两大类
  • 在标签的尖括号之间使用,直接用双花括号嵌套 props 名称即可

    <button class="btn">{{text}}</button>
    
  • 在标签的属性内使用,需要使用 vue 指令 v-bind: 也可以直接省略使用 :

    <!--  语法就是 v-bind:属性名='这里面直接写js即可'  v-bind: 可以简写成 : -->
    <!-- <button :style="'background-color:'+bgColor " class="btn"></button> -->
    <button :style="`background-color: ${color}`" class="btn">按钮</button>
    

vue 的模板语法

我们在介绍 prop 的时候就已经使用了 vue 的模板语法。在 template 中嵌入 js。
分为两大类

  • 在标签的尖括号之间使用,直接用双花括号嵌套 props 名称即可
  • 在标签的属性内使用,需要使用 vue 指令 v-bind 也可以直接省略使用 :

复用技巧的小整理:

  1. 只要是在 template 想要使用 js 的话都遵循两个语法,也就是以上两大类
  2. v-bind:属性名='这里面直接写js(只能是值或者运算不能是语句)即可
  3. <button :class="btn ${isActive ? ‘color’ : ’ '}"></button>

vue 组件的 data

  • 只要是组件的 html 内容(结构,样式…)发生改变的话,那么这个改变必须由 data 内的某个 数据 控制。
  • 之前想要点击按钮修改颜色,必须获取真实的 box 的 dom 节点,然后修改
  • 其实上述方案可能会导致浏览的重排和重绘
  • vue 为了解决上述问题使用的 虚拟 dom 的概念
  • 页面的所有的变化必须由组件的 data 的改变而变化

使用方法

  1. 需要将变化对应的内容设置成 data
export default {
  name: 'App',
  data: function () {
    return {
      bgColor: 'red',
    }
  },
  ...
  1. 将 data 应用到页面中,使用的时候就相当于一个变量
<div v-bind:style="`background-color:${bgColor}`" class="box">
  <button>单机变蓝</button>
</div>
  1. 使用 js 逻辑修改对应的 data 页面自然就会跟着变化了
  • data 的使用
    • 在 template 中使用直接当成变量,使用模板语法写到标签内即可
    • 在 script 内使用的时候要用 this.名 访问
  • data 的修改
    • 在 template 中的函数内直接对 data 赋值即可
    • 在 script 函数内使用 this.名 重新赋值,这种表较常用

vue 组件内的事件绑定

直接使用 v-on 指令绑定事件,也可以简写成 @ ,vue里没有hover事件,可以写成mouseenter事件

<button v-on:click="change()">单机变蓝</button>
<button @click="change()">单机变蓝</button> 
 <!-- @事件类型='事件函数(可以使 methods 内设置的,也可以是直接写的)' -->
<!-- 所有vue指令的值的引号内都可以直接写 js 语法 ,@click就算是一种指令,后面的值里可以用js -->
<!-- 以上需要写methods对象,以下不需要写methods对象  -->
<button @click="function(){bgColor='blue'}">单机变蓝</button> 
<button v-on:click="()=>{bgColor='blue'}">单机变蓝</button>
 <!-- 事件函数内只要有修改 data 的操作,那么上述写法可以直接简化成: -->
<button v-on:click="bgColor='blue'">单机变蓝</button>

<span>成绩:{{grade}}</span>
<br />
// 由于函数没有返回值所以update(190)的返回值是undefined,而vue指令的功能可以这样写,还可以写成: -->
// <button v-on:click="function(){update(200)}">修改成绩</button> -->
// 以上 function是事件函数,update是普通函数 -->
// 没有function函数,update无论有参没参都是事件函数 -->
// 只要是事件函数就有event参数,可以直接用 -->
<button v-on:click="update(190)">修改成绩</button>

change 是一个函数名,该函数必须声明在,组件导出的对象下的 methods 属性内,注意的是 这里面的函数在 template 内使用的时候直接使用方法名,而在 script 中使用的时候需要使用 this.方法名

  methods: {
    // 该对象下的属性需要写成函数,这个函数可以直接当作事件函数,也可以当作普通函数
    change() {
      this.bgColor = 'blue'
    },
    update: function (value) {
      // update: () => {
      // console.log(this.grade)
      // 箭头函数:Cannot set property 'grade' of undefined"
      //箭头函数和普通函数的this指向不一样,所以箭头函数出错
      this.grade = value;
      console.log(this)
      // 普通函数的 this 指的是组件实例,指的就是这个组件本身
      // 我们可以直接通过 this 能访问到  组件的 data methods  props ...
      console.log(event);
      // 这个事件函数不用传参,直接能用,event显示一个mouseEvent的对象,这个event是从事件函数那来的
    },
  },

组件间的通信

爷孙组件

  • 把del方法使孙子用,爷组件用:delete=“del”,儿子接收@delete=‘delete’,孙子直接用 @click=“delete”
  • 需要注意,不能用delete关键字,以上我懒的改了!

父子组件

  • 使用 props,props 一般用来传递值,也可以传递函数(一般不使用)
<Span :slide1="slide" text="asdsdfsf" />
  • 自定义事件, 向子组件传递函数,一般是当父组件的 data 想要让子组件修改时使用

  • 给子组件设置 ref

    父组件中

    <!-- content是组件名 -->
    <Content ref="ceShi" />
    <button @click="handle">测试ref</button>
    
    handle() {
      this.$refs.ceShi.xxx();
    },
    

    子组件中

    xxx() {
      console.log(111);
    },
    
  • 子组件也可以传递给父组件函数让父组件使用

    子组件内

    <button @click="$emit('add',clearInput)">按钮</button>
    
    methods: {
      clearInput() {
        console.log(111)
      },
    },
    

    父组件内:

    methods: {
      add(callBack) {
        //在add里用的时候当作函数调用
        callBack()
      },
    }
    
  • 父组件内使用 $children 可以获取所有子组件的实例组成的数组

    //只能在mounted里log,因为mounted渲染完毕了。
    mounted() {
      console.log(this.$children[0]);
    },
    
  • 子组件内使用 $parent 获取父组件实例

    <!-- 也可以不传props直接使用,但是推荐用props。 -->
    v-for="item in $parent.shuJi"
    
    <!-- 直接使用爷爷的事件: -->
    <button @click="$parent.$parent.del(shuJi.id)" 
    
    mounted() {
      console.log(this.$parent);
    },
    
  • 插槽(slot)

    1. 默认插槽
    <!-- 父组件 App-->
    <Button v-model="isActive">哈哈哈</Button>
    <!-- 子组件Button ,用stot接收,页面直接显示 哈哈哈-->
    <button>
      <slot></slot>
    </button>
    
    1. 具名插槽
      <!-- 具名插槽 v-slot:list 可换成#list  ,父组件App-->
      <template #list>
        <div class="list">列表</div>
      </template>  
      <!-- 子组件Container,主要用来做布局的,可以用以上方式来命名分类,接收用name接收 -->
      <div>
        <slot name="list"></slot>
      </div>
    
    1. 作用域插槽
      <!-- 为了让子组件的内容在父组件的插槽中使用: -->
      <slot v-bind:obj1="obj" />
      <!-- js里定义了对象 obj -->
      <!-- 以下是父组件中,接收的是obj1 -->
      <ScopedSlotDemo>
      <!-- 可以把以下换成 slot-scope="{ obj1 }",但是slot-scope马上就被废弃了-->
        <template v-slot:default="slotProps1">
          <span>{{ slotProps1.obj1.age }}</span>
        </template>
      </ScopedSlotDemo>
    
      <!-- 独占默认插槽简写如下,也可以去掉:default -->
      <ScopedSlotDemo v-slot:default="slotProps">
          <span>{{ slotProps.obj1.age }}</span>
      </ScopedSlotDemo>
    
      <!-- slotProps 接收的是一个对象,可以解构 -->
      <ScopedSlotDemo v-slot:default="{obj1}">
        <span>{{ obj1.age }}</span>
      </ScopedSlotDemo>
    
  • 子组件想要修改父组件的内容,类似 sync,语法如下:

    父组件内:把obj全部的属性传给子组件

    <Son v-bind.sync="obj"></Son>
    
    data() {
      return {
        obj: {
          a: 10,
          b: 100,
        }
      };
    },
    

    子组件内:可以接收obj对象的全部属性或部分属性,用update进行修改,改成了111

    <button @click="$emit('update:b', 111)">修改父组件的b</button>
    
    export default {
      props: {
        b: {
          type: Number,
        }
      },
    };
    
  • 在组建上使用 v-model 指令,其实就是相当于将 props(:value) 和 自定义事件 (@input) 简化了. input 事件就是修改 value 的方法(v-model写在组件上,子组件想接收并修改props接收的值,实际上接收value就可以了,不能换成其它的属性)

    父组件内:定义number为一个值

    <Son v-model="number"></Son>
    

    子组件内:这样就把父组件number的值修改了,变 200

    <div>展示父组件内的 number: {{ value }}</div>
    <button @click="$emit('input', 200)">用v-model修改</button>
    
  • provide 和 inject

    父组件内

    <div>父组件的 provide: {{ x }}</div>
    <!-- 上面的语句会报错,因为父组件内拿不到自己provide里的值  -->
    <button @click="x = 20">修改provide里的x的值</button>
    
    // 和data同级
    export default {
      provide: {
          x: 100,
      },
    }
    

    子组件内

    <!-- 用inject来接收父组件provide的值并可以展示 -->
    <div>使用父组件 provide 的数据:{{ x }}</div>
    
    export default {
      name: "Son1",
      inject: ["x"],
    };
    

    为了让父组件能拿到自己的值,我们一般写成函数,如下:
    父组件内

    <div>父组件内的 number:{{ number }}</div>
    <!-- 在父组件中修改number的值,number变了,但是provide里的x值是不会变的 -->
    <!-- provide 提供的数据是不可响应的 -->
    <!-- 如果想提供相应数据,可以将数据做成响应的Vue.observable -->
    <button @click="number = 20">修改provide里的number的值</button>
    
    export default {
      data() {
        return {
          number: 1000,
        };
      },
      provide() {
        return {
          x: this.number,
        };
    },
    

    子组件内

    <div>使用父组件 provide 的数据:{{ x }}</div>
    
    export default {
      inject: ["x"],
    };
    

    还有一种情况就是我们想在created里修改数据内容,但是子组件是不会接收到在里面修改的内容,如下:
    父组件:

    data() {
      return {
        obj: {
          name: null,
        },
      };
    },
    created() {
      this.obj = { name: "小红" };
    },
    provide() {
      return {
        x: this.obj,
      };
    },
    

    子组件:

    inject: ["x"],
    created() {
      console.log(this.x);
      // 拿到的值是null
    },
    

    解决以上问题,为了让子组件能拿到created里更新的数据:在父组件里定义一个方法,方法返回obj,在把方法赋给provide,如下:
    父组件:

    created() {
      this.obj = { name: "小红" };
      // 这里是异步,子组件也能拿到
      // 在父组件里修改obj,子组件也会变,就是把数据变成可响应的了
    },
    provide() {
      return {
        x: this.getObj,
        // x: () => this.obj
        // 简化方法,然后可以把下面 methods 去掉
      };
    },
    methods: {
      getObj() {
        return this.obj;
      },
    },
    

    子组件:

    inject: ["x"],
    // 子组件这样也可以对父组件的值进行修改
    created() {
      console.log(this.x());
      // 拿到小红
    },
    
  • a t t r s , attrs, attrslisteners

    app内:

    <Parent2 :a="10" obj="小花" @x="() => console.log(1)" />
    <!-- 除了style和class的样式不能传之外,props里的所有属性都可传,直接在子组件里log(this.$attrs和$listeners),就能拿到props和事件 -->
    

    Parent2内:

    <Son2 v-bind="$attrs" v-on="$listeners" />
    <!-- v-bind 和 v-on 是给它的儿子组件传的,在它儿子里log,也能直接出现props和事件 -->
    

兄弟组件

  • 将兄弟间需要相互使用的 data 共享到父组件内,那么兄弟之间的交互就变成了父子之前的交互了

自定义事件

  • 父组件向子组件传递函数需要使用自定义事件,其实也可以直接使用 props,自定义事件名称推荐使用ebab-case 命名,不使用 camelCase 方式(驼峰)
    父组件内
    语法: @自定义事件名称=“父组件内的函数名称”
<!-- 第一种方式时直接将函数传递过去 -->
<Button @change1="change"></Button>
<!-- 第二种方式是将带参数的函数传递过去 -->
<Button @change1="change(index)"></Button>

子组件内
js 内

  methods: {
    handleClick() {
      // 接收父组件传递过来的函数并调用
      this.$emit("change1");
      // 这是传参调用
      // this.$emit("change1",4);
    }
  }
  • 当你要实现子组件同步父组件的 data 时,一般采取父组件内定义事件传递给子组件执 行的方案,此方案可以使用 .sync 修饰符简化
    自定义事件的基础写法,组件名写成 update:title
    父组件内的两种自写法

    <!-- 还有一种就是将事件函数定义在父组件内的 methods 内 -->
    <!-- 下面的这个写法 $event 代表的就是子组件调用函数传递的参数 -->
    <Box :title="title" @update:title="title = $event"></Box>
    

    简化

    <!-- 上面写法的语法糖 -->
    <Box :title.sync="title"></Box>
    

    子组件内

    this.$emit("update:title", "新的值");
    

    tenplate 内

    <button @click="handleSlide">
      {{ text }}
    </button>
    
    <button @click="$emit('changeSearchValue',1)" />
    

自定义事件还有一个修饰符 .native ,该修饰符的作用就是将自定义事件直接绑定在子组件的根元素标签上,但是这类自定义事件名称必须和原生事件名称相同。

<!-- 直接给子组件 Button 的根元素绑定了 click 事件 -->
<Button @click.native="change"></Button>

小提示

  1. 子组件想要修改父组件的 data ?

    • data 只能在自己的组件内使用 this.dataName 修改,想要让其他人修改自己组件的 data ,那么需要先在自己组件内定义好修改的方法,然后将该方法传递给其他组件去执行
  2. 为什么选择一个 data 不设置两个?

    • 因为只要定义了一个 data 那么 vue 就有针对这个 data 做一套虚拟 dom 流程
  3. 如果图片的 src 地址写成了 js 相关的值,那么图片不会解析在浏览器上,解决方案:

    • 换成网络地址 推荐
    • 将 js 生成的图片地址使用 require(js地址) ,只能在 script 标签中使用
    html
    <!-- 用变量的话这样用 -->
    <li :locate="require('../'+item.locate+'.jpg')"></li>
    
    js
    liArr:[{locate:"assets/p1"}]
    
  4. 以后我们自己做项目的话可能会用到一些图片,这些图片写成本地的话不太好,将我们的图片上传到 github 或者 腾讯云 或者其他的免费网站

  5. 报错 Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. 意思是:请避免直接修改 prop ,在父组件重新渲染的时候该属性会被重写(避免在子组件内修改父组件传过来的data)

  6. 在 template 中当事件函数内想要使用事件对象请使用 $event

    <input type="text" @input="$emit('change-searchvalue',$event.target.value)" /> 
    <!-- 传的参数是dom节点的值 -->
    
  7. 三目运算符的优先级低于 &&

  8. if语句的简写方式,条件成立时,执行后面的语句,后面的语句必须和if在同一行

    if(value) console.log(111),(console.log(222))
    else console.log(333)
    
  9. 想按回车就添加,就v-on:keyup 或者可以keydown都可以,回车的编码是13,把enter 换成13也对

    <input
    type="text"
    v-model.trim="value"
    @keyup.enter="handleClick"
    />
    
  10. gitbash 不可用,使用 powershell 运行 vue ui 提示

    无法加载文件 C:\Users\sunnyzz\AppData\Roaming\npm\vue.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https
    :/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
    

以管理员身份打开命令行输入 Set-ExecutionPolicy RemoteSigned 然后输入 Y 。再次运行 vue ui 即可

  1. 字体图标和自动生成代码(自定义代码片段)的视频是2020.8.31

  2. 生命周期是同步的,发送请求是异步的

  3. bootcdn->twitter-bootstrap:字体样式,复制到index.html中即可使用字体

  4. 想要使用axios插件,需要在依赖下安装,在代码中引入

    import axios from 'axios'
    
  5. computed 该计算属性是在页面初始的时候就执行,所以在父组件的 computed 内无法获取子组件实例,也就是不可以使用 $refs 和 $children(因为子组件的created还渲染完,涉及到生命周期问题),computed里也不能做异步操作,不允许。

  6. 想要有间隔的触发,而不是每点击每触发 (京东,老师推荐用getTime)

var preTime = 0
  $('button').click(function (event) {
    // 格林威治时间
      var nowTime = new Date().getTime()
      // 下面这种第一次点击的时候有可能不触发,因为时间有可能不大于1秒,所以得加上判断
      // var nowTime = event.timeStamp
      if (nowTime - preTime > 1000) {
          //    if(nowTime - preTime > 1000 || preTime === 0){
          console.log('即便点的快,也一秒秒的触发(京东)');
          preTime = nowTime
      }
  })
  1. get是请求(res是整个数组),delete是删除(什么也不返回res是空的),patch是修改(res返回修改之后的那条对象,而不是整个对象),post是添加(res返回要新加的那一项),还有一个put(put和post都是修改,区别是put是更新,也就是更新所有项,而post是修改,可以只修改需要的某项)

    axios.get("http://localhost:3000/books").then((res) => {
      console.log(res.data)
    // 整个数组
    });
    
    axios.delete("http://localhost:3000/books/" + id).then((res) => {
      console.log(res.data)
    // {} , 输出空的。删除这条id和对象
    });
    
    axios.patch("http://localhost:3000/books/" + id, newBook).then((res) => {
      console.log(res.data)
      // 返回修改之后的那条对象也就是newBook,而不是整个对象
    });
    
    axios.post("http://localhost:3000/books", newBook).then((res) => {
      console.log(res.data)
      // 返回要新加的那一项,也就是newBook
      this.shuJi.push(res.data);
      // 想要显示在页面上,还需要添加上这一条对象,要不然是后台更新了,前端还没更新,不过一刷新就又有了
    });
    
  2. 代码片段:按Fn+F1,输入snippets,选择首选项代码片段那个,然后选择vue.json, 默认有个log,的代码片段。地址[https://blog.csdn.net/maokelong95/article/details/54379046]

      "Print to console": {
        "prefix": "log",
        "body": [
          // $1代表的是光标,${TM_FILENAME_BASE代表组件名
          "console.log('$1');",
          "$2"
        ],
        "description": "Log output to console"
      }
    
  3. 当每个按钮对应每个点击事件时,没有必要写三个事件,可以写成一个 xx(属性名,属性值){this[名]=值}

    <button @click="x(one,'one1')">按钮</button>
    <button @click="x(two,'two1')">按钮</button>
    
    methods: {
      x(name, zhi) {
        this[name] = zhi;
      },
    },
    
  4. Object.defineProperties 给对象添加一个或多个属性或方法,或者修改现有的属性

    const obj = { a: 10, b: 30 }
    Object.defineProperties(obj, {
      b: {
        get() {
          return 20;
        }
      },
    });
    console.log(obj.b);
    
  5. 运行依赖:项目必须(运行)依赖(axios vue-router element-ui …) ,项目没有这个包不可能运行 --save -S
    开发依赖:(sass less stylus …) 工具类的(编译工具,插件类辅助工具) --save-dev -D

  6. addEventListener和click的区别在于:两个click后面的会覆盖前面的,而两个addEventListener都触发,removeEventListener只能解绑addEventListener触发的事件

    const fun = function(){
      console.log(11)
    }
    document.addEventListener("click", fun);
    document.removeEventListener("click", fun);
    
  7. 22的补充:这种情况下就解绑不了,因为fun是一个函数,这个函数返回一个新函数,函数是一个对象,所以两次的地址不一样,返回的就不一样,就解绑不成功)

    var fun = ()=> function(){
      console.log(11)
    }
    document.addEventListener("click", fun());
    document.removeEventListener("click", fun());
    // 不成功,想要解决用以下方案
    var fun = ()=> function(){
      console.log(11)
    }
    var a = fun()
    document.addEventListener("click", a);
    document.removeEventListener("click", a);
    
  8. 模块内自带作用域,如果没有导出,别处是拿不到模块内的变量的

  9. 组件内部想要调用自己必须先声明name属性,当作标签名,外面必须加判断

    <!-- 这个例子会提示异常,因为调用太多了 -->
    <template>
      <div>
        <div v-if="num > 4">
          <Cascader />
        </div>
      </div>
    </template>
    <script>
    export default {
      name: "Cascader",
      data() {
        return {
          num: 5,
        };
      },
    };
    </script>
    <style></style>
    
  10. Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

    属性 writable 是否能修改,默认为false

    const obj = {
        name: '小黑'
    }
    Object.defineProperty(obj, 'age', {
        value: 10,
        writable:false
    })
    obj.age=11
    console.log(obj)
    // {name:'小黑',age:10}.因为writable是false,所以没有修改
    

    属性 get set(类似监听)

    const o = {}
    Object.defineProperty(o, 'a', {
        value: 10
    })
    let bValue = 'hello'
    Object.defineProperty(o, 'b', {
      // 这里不能和vaue等其它属性一起用
        get() {
            return bValue
        },
        set(newValue) {
            bValue = newValue
        }
    })
    bValue = 100
    console.log(o);
    // {a:10,b:100},通过 get 接收,通过 set 修改
    

    玫举属性 enumerable

    const o = {}
    Object.defineProperty(o, 'a', {
        value: 10,
        enumerable: true
    })
    Object.defineProperty(o, 'b', {
        value: 15,
        enumerable: false
    })
    Object.defineProperty(o, 'c', {
        value: 20,
        enumerable: true
    })
    // for..in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性
    for (var key in o) {
        console.log(key);
        // 结果 a c,因为b的玫举是false,所以这里不显示b
    }
    
  11. 获取到当前页面锚点链接(#)

const path= window.location.hash
console.log(path)
  1. vue 类的声明文件:便签例子中($message 编译总是报错, src 下加一个 vue.d.ts)
import Vue from 'vue'
import { ElMessage } from 'element-ui/types/message'
import { ElMessageBox } from 'element-ui/types/message-box'

declare module "vue/types/vue" {
    interface Vue {
        $message: ElMessage;
        $messageBox: ElMessageBox
    }
}
  1. 查看密钥的方法:在命令行输入 cd ~/.ssh/ 然后输入 cat id_rsa.pub ,这就是密钥
  2. input[value= ] ∗ 5 , 再 点 t a b 生 成 的 是 5 条 i n p u t , v a l u e = ′ 1 ′ − ′ 5 ′ , [ v a l u e ] 是 属 性 , ]*5 ,再点tab生成的是5条 input,value='1'-'5',[value]是属性, ]5tab5inputvalue=15,[value]是内容
  3. button{$}*5,再点tab生成的是5条button,{}是指里面的内容(不理解的话试试就知道了)
  4. button li span 里的内容是text(),别的是val(),html()通用(input除外);赋值 ${‘input’}.val(66)
  5. 解除事件绑定 jquery 的 off 方法,彬按钮修改按钮的 disabled 属性
$('.btn1').off('click');
// off方法,能单机但不管用
$('.btn1').attr('disabled', true)
// 不能单机,变灰色
  1. 清空: ( ′ . l i s t ′ ) . e m p t y ( ) 删 除 : ('.list').empty() 删除: (.list).empty()(‘.list’).remove()
  2. setInterval 可以执行事件函数,下面是原生例子,jquery也可。
document.querySelector('.right').onclick() = function(){
  console.log('一直在执行,取消用clearInterval')
}
setInterval(function(){
  document.querySelector('.right').onclick()
  // jquery   $('.right').trigger('click')
  // $('.right').click()  也行,但是不太好
},1000)
  1. 事件委托:将后代的事件委托给自己的祖先(京东例子)
// ul 是变量,用 tagName 可以获得子级 li(得到的标签名是大写的) 的 this,再操作 this
ul.onclick = function(event){
  if(event.target.tagName==='LI'){
    // 得到的是点击的这个 li 
    console.log(event.target)
  }
}
  1. 事件冒泡规则: 子级和祖先都写事件,鼠标划过子级,祖先们的事件也会触发,原生的onmouseenter(它是一个特例)是从外(大)到内(小),别的(onclick、js)是内到外(京东例子)
  2. 阻止事件冒泡event.stopPropagation():原本单击子级出现’子级’‘父级’
$('div').click(function () {
    console.log('父级');
})
$('span').click(function () {
    console.log('子级');
    // 阻止了之后单击子级只出现'子级'
    event.stopPropagation()
})
  1. vue 中事件修饰符 stop 阻止事件向上触发(在父级中写的click事件,在子级中也会触发,想要阻止就用stop)
<div @click.stop class="dialog">
  1. 阻止元素的默认行为:form 里的button 默认就是submit,想要需要写成如下:
    <!-- 1. event.preventDefault  阻止默认行为  a的超链接 表单的默认提交和重置  -->
    <!-- 2. return false -->
    <button @click.prevent>确定</button>
    
  2. 阻止a的跳转:href=‘javascript:;’ href=‘javascript:void(0);’
  3. 父级节点:parentNode 如:this.parentNode
  4. 想让 a 跳转,并在新页面打开,需要 target=“_blank”
<a href="https://www.baidu.com" target="_blank">跳转</a>
  1. for in 语句:对象的遍历
var user = {
  name: 'lucy',
  like: 'eat',
  say(){
    console.log('说')
  }
}
for (var i in user) {
  // i 代表对象的3个属性名
  // user[i]代表对象的 3 个属性值
  console.log(i,user[i])
}
  1. for of 语句:就是一个for循环
var arr = [1, 2, 56, 2, 1, 41, 1, 1, 23, 1, 12, 12, 1, 21, 12123]
for (var i of arr) {
  console.log(i) // 依次输出值
}
  1. 空数组也是真
var a = []
if (a) {
  // 只有确定的那几个数是false 别的都是true,空数组也是true,log 1
  console.log(1)
  // 数组 a 里没有 b ,输出 undefined
  console.log(a.b)
}
  1. document.querySelector(‘li’) 虽然看上去是一个标签,但实际上是一个对象包括属性(className、target等等),所以可以给li添加属性
var liArr = document.querySelectorAll('li')
// 因为事件绑定刚进页面就绑定成功了,在循环里i一直是length(4)
// 所以可以用加属性名的方法来解决i的值
for (i = 0; i < liArr.length; i++){
  li.index = i
  liArr[i].onclcik = function(){
  console.log(i)   // 输出 4 4 4 4
  console.log(this.index) // 这样输出就是 0 1 2 3 ...
  }
}
  1. 函数内默认存在一个关键字 arguments 代表的是存储实参的类数组
function people(name) {
  console.log(arguments[0]);
  // 'zhang' ,arguments是一个数组,内容是传过来的参数
}
people('zhang')
  1. jquery 的css 可以获取元素的行内样式值(老师测试的时候是行内,我测的是最终,和50一样)
console.log($('.box').css('background-color'))
// 获取css的背景色,如果box重名,永远获取第一个盒子
// 先获取行内样式,没有再获取上面的样式
$('.box').attr('class','xxx')
// 修改class的类名为xxx
  1. 获取该元素的所有最终样式对象
// window.getComputedStyle(元素,null)
var box = document.querySelector('.box')
window.getComputedStyle(box,null).width

列表渲染

将一组数据循环渲染到页面上,需要使用指令 v-for
循环渲染两种情况

  • 带数据的(有 data)
    <!-- 想要使用下标的话  (item,index) in arrList -->
    <li v-for="(item,index) in arrList" :key="index">{{item}}</li>>
    
     <!-- key 属性是必须的 而且该循环下唯一 -->
    <li v-for="item in arrList" :key="唯一值">{{item}}</li>
    
  • 就是循环很多次,不需要 data。这种很少见
    <li v-for="item in 10" :key="唯一值">{{item}}</li>
    

条件渲染

使用两个指令

  • v-show 样式的消失和出现
    <div v-show="listArr.length">
    <!-- 数组有值的情况下显示 -->
    
  • v-if 可能搭配 v-else v-else-if 结构真正消失,需要注意 if else elseif 使用的时候必须紧跟着中间不允许出现其他的元素,而且该指令对应的元素都是兄弟元素
    <span v-if="ind==0">1</span>
    <span v-else-if="ind==2">2</span>
    <span v-else-if="ind==3">3</span>
    <span v-else>4</span>
    
  • 消失出现次数比较频繁使用 show, 消失出现次数比较少使用 if(show 只是显示隐藏,if就是有或没有,老师推荐使用v-show)

style 和 class 绑定

style 行内样式 和 class 名在 vue 组件内的多种写法,其实就是为了更简单的去实现样式的修改。
style 绑定

  • 默认的字符串方式
  • 对象表示法
    <!-- 古老的修改style的方法,因为fontSize中间有-,所以要加引号 -->
    <!-- <div class="box" :style="{'font-size': '50px'}">style</div> -->
    <div class="box" :style="styleObj">style</div>
    
    data: function () {
      return {
        isHas: true,
        styleObj: {
          color: "red",
          fontSize: "50px",
        },
      };
    },
    
  • 数组表示法((较麻烦不好用,老师没讲))

class 绑定

  • 默认的字符串方式
    <div class="box"></div>
    
  • 对象表示法
    <div :class="{box:true,active:true}">style</div>
    
  • 数组表示法
    <div :class="['box',isHas?'active':'']"></div>
    <div :class="['box','active']"></div>
    
  • 数组内嵌套对象表示法
    <div :class="['box',{active:true}]"></div>
    

vue 的表单

vue 将表单的输入(文本)绑定成组件的 data ,用组件的 data 去控制。vue 提倡使用 v-model 指令去实现表单的绑定。我们也可以使用 value 配合 input 或者 change (.lazy) 事件替代 v-model 指令。

老方案:

<input type="text" @input="handleChange" :value="username" /> 
// data里,
username:"名字"
// 事件里,
handleChange(){
  // 先获取真实 dom 节点  event.target
  this.username = event.target.value;
}

新方案:

<!-- change 对于 input:text | password 是属于失去焦点事件 -->
<!-- input 对于 input:text | password 是域的内容发生改变就会触发  -->
<!-- 使用 v-module 简化表单的双向绑定 -->
<input type="text" v-model="username" />
data:function(){
  username:'名字'
  //此时username随便内容变化而变化,不用写事件
}

v-model 指令的修饰符

  • .lazy 实现懒加载将 v-model 的默认事件从 input 改成 change
  • .number 自动将输入的值使用 parseFloat() 转化成数字类型
  • .trim 自动将输入的值的左右空白去掉
    <input type="text" v-model.trim="password" />
    

vue 组件的计算属性

你想对一个 data 进行复杂的逻辑操作后在放到页面上展示,此时我们可以直接将复杂的操作写在模版语法内,或者使用函数操作之后返回想要的值。但是 vue 组件提供了更好的方法 就是组件的计算属性(computed),用法和 data 一致

// 计算属性就是一个函数,该函数必须返回一个值,这个值就是计算属性,计算属性函数不可以传递参数
computed: {
    typeList() {
      return this.arr.reduce((res, item) => {
        if (!res.includes(item.category)) {
          res.push(item.category);
        }
        return res;
      }, []);
    }
  },

reduce是数组的遍历,箭头函数可以不写return,而对于返回对象来说的说,可以去掉return,把{}换成(),res是最终的结果,item是遍历的每一项, { totalPrice: 0, totalNum: 0 }是res的初始值,函数里要返回新的结果来继续遍历,遍历之后res为最终的结果。

computed: {
  total() {
    return this.products.reduce(
      (res, item) => {
        return {
          totalPrice: res.totalPrice + item.price * item.quantity,
          totalNum: res.totalNum + item.quantity,
        };
      },
      { totalPrice: 0, totalNum: 0 }
    );
  },
  // total() {
  //   return this.products.reduce(
  //     (res, item) => ({
  //         totalPrice: res.totalPrice + item.price * item.quantity,
  //         totalNum: res.totalNum + item.quantity,
  //     }),
  //     { totalPrice: 0, totalNum: 0 }
  //   );
  // },
},
// 一会研究一下!
const newArr = new Set(arr.map(item => item.category));

当你的一个计算属性想要实现反向操作的时候,意思就是直接给计算属性重新赋值,然后让计算属性的来源 data 被修改。此时可以给计算属性设置 setter ,代码如下

// firstName 和 lastName 是组件的 data
// 当计算属性存在 setter 的时候计算属性就写成了 对象类型
computed: {
    fullName: {
      get() {
        return this.firstName + " " + this.lastName;
      },
      set(newValue) {
        // newValue 代表新的计算属性 或者叫更改之后的计算属性
        // setter 用来修改计算属性的来源 data 的
        this.firstName = newValue.split(" ")[0];
        this.lastName = newValue.split(" ")[1];
      }
    }
  },

vue 组件的侦听器 watch

当你的计算属性需要根据异步操作来计算,但是计算属性函数内要直接返回结果,不能添加异步操作。所以可以使用 watch 实现。
使用案例

// question 和 answer 是组件的 data,answer 要随着 question 的变化而变化,但是如何变化要发送请求才知道
// watch 对象下有几个属性
// 第一个 handler 是数据变化的触发的函数
// 第二个 immediate 在组件初始化的时候就执行一次 handler
// 第三个 deep 为了发现对象内部值的变化,可以在选项参数中指定 deep: true。
watch: {
    question: {
      // 监听 question 修改 answer
      handler() {
        // 当 question 发生变化是就会执行
        if (this.question) {
          setTimeout(() => {
            // 向后台获取答案
            this.answer = Math.random();
          }, 100);
        }
      },
      // 进入页面就执行一次
      immediate: true
    }
  }

vue 组件的 ref

当你想在 vue 组件内获取一个元素的真实 dom 结构的时候,可以使用原生方案 document 一套,也可以借助插件(没讲)。但是呢,vue 其实提供了一个方案,就是组件的 ref。一般获取真实 dom 节点用于获取某个值,并不是用于修改。

<button ref="btnDom"></button>
this.$refs.btnDom;
// 就是获取了 button 的原生 dom

其实 ref 也可以直接写在组件上,那么获取的就是该组件实例,也就是说可以获取组件内的所有内容。进而也就可以实现父子组件之间的交互。

<li ref="ceShi"/>
<!-- li是组件 -->
console.log(this.$refs.ceShi.arr)
//this.$refs.ceShi 相当于该组件里面的this.arr
// 也可以调用子组件的方法
this.$refs.ceShi.方法名()

组件内的 this

组件内使用 this 其实就是想要使用该组件下的 data props methods computed 自定义事件 ref … ,那么组件内我们通常使用的 this 就指的是该组件的实例(VueComponent)。
如何获取组件实例

  • methods 将函数设置成普通函数,该函数的根作用域下的 this 就是组件实例.如果根作用域内的其他子作用域想要直接访问 this 那么请设置成箭头函数(箭头函数下的this 就是vue组件,而普通函数下的this就是调用的window,比如,settimeout)
  • computed 跟上面 methods 一样
  • 生命周期函数跟上面一样

组件的生命周期

组件从出现到渲染页面或者再页面中销毁,各个阶段 vue 都提供了对应的函数,供开发者使用。这些函数被称作生命周期钩子。生命周期钩子是同步函数。

初始渲染阶段

  • beforeCreate
  • created: data 等其他组件的数据处理完毕,可以在页面初始的时候发送请求更新数据。发送请求使用的是 axios 插件。
  • beforeMount
  • mounted: 组件在页面渲染完毕,可以获取组件内的真实 dom 节点。

数据更新阶段

  • beforeUpdate 数据更新时调用,发生虚拟 dom 打补丁之前
  • updated 数据更新完毕,dom 渲染完毕

组件的销毁阶段

  • beforeDestory 组件即将被销毁,并不是组件的内容在页面上消失

  • destoryed 组件销毁完毕,v-if属于销毁,v-show不属于,需要写在组件内,销毁的是组件。 我们在这个生命周期内,可以手动解除一些跟该组件的无关的一些操作(setInterval 跟浏览系相关的一些事情,setInterval属于浏览器,所以即便组件销毁了,setInterval也不会停止)

    // 生命周期
    // 阶段 一
    // 初始渲染阶段 (其实就是页面刚进入的时候或者刷新的时候)
    // 该阶段需要实现的大概有 获取页面初始数据(进入页面就向后台获取数据然后更新页面,或者其他的一些进入页面就需要做的事)
    beforeCreate() {
      console.log("组件刚被创建,在初始化data之前");
    },
    created() {
      console.log(
        "组件的数据观测 (data observer),property 和方法的运算,watch/event 事件回调,配置完毕"
      );
      // 此阶段最适合进入页面就修改 data ,发送请求获取后台数据,修改 data
      axios.get("http://localhost:3000/articles").then(res => {
        // console.log(res.data);
        setTimeout(() => {
          this.articles = res.data;
        }, 1000);
      });
    },
    beforeMount() {
      console.log("组件即将要渲染,挂载之前");
    },
    mounted() {
      console.log("渲染执行完毕");
      // console.log(document.querySelector(".title"));
      // 刚进入页面想要获取真实的 dom 节点做一些功能,在此阶段可以实现,但它在异步之前就执行完毕了
      // 比如说 swiper 插件  使用的时候  new Swiper('.container')  需要获取真实 dom 节点 container
    }
    

axios 封闭

app.vue
// 其中地址可以写成空,因为可以设置默认地址,有时也可以删除,切记一一对应
$http("delete", id) //id对面自动连接上
$http("post", "http://localhost:3000/books", newBook
$http("get", "http://localhost:3000/books")
$http("patch", newBook.id, newBook)
//可以传过去对象,对面接收语法是config,
$http({ method: "delete", url: id });
$http({ method: "post", data: newBook })
$http({ method: "get" })
$http({ method: "patch", url: newBook.id, data: newBook })

//可以传三个参数过去

axios.js
// 先是导入js,两种方式
import $http from "./axios"; // 默认导入
import { $http } from "./axios"; // 命名导入

// 封装 axios 请求
// 将请求整合到一处便于管理
// 默认导出
import axios from "axios"
// axios 请求就会返回一个 promise
// 当你的请求都是基于一个服务器下的时候,可以给请求设置基地址
// 配置 axios 请求的默认基地址
axios.defaults.baseURL = "http://localhost:3000/books"
// export default (type, url, params) => {
//     return axios[type](url, params)
// }
export default config => {
    return axios(config)
}
// 将函数的参数设置成对象的话比较好
// 因为将对象拆分成三个参数的话必须传递三个参数才能一一对应.
// 对象是有属性名的,属性名对应即可

//命名导出
const $http = (type, url, params) => {
    return axios[type](url, params)
}
export { $http }

动态组件

当你想要根据一个数据切换不同组件的展示,此时可以使用动态组件。动态组件是由 vue 的自带 component 元素搭配 is 属性代码如下
动态组件的切换方式是属于 v-if 的切换

<!-- currentComponentName属性 的值需要和组件名相同 -->
<!-- 当修改 currentComponentName属性 的时候就会切换不同的组件展示了  -->

<!-- <Home v-show="activeType==='Home'" />
<Posts v-show="activeType==='Posts'" />
<About v-show="activeType==='About'" /> -->
<component :is="currentComponentName"> </component>

动态组件搭配 keep-alive 实现动态组件的数据缓存。允许组件有条件的缓存(include,exclude,max)
当动态组件切换的时候每个组件默认都会变成初始状态,假如有的组件内有 data 并且希望 data 修改的时候能够保留,意思就是动态组件切换的时候中间的某个组件的 data 不会被初始化。此时就需要使用 keep-alive

<keep-alive include="Home" exclude="Home" :max="10">
  <!-- includes 属性的属性值可以是字符串或者正则,匹配的组件名才缓存数据 -->
  <!-- exclude 与 includes 相反 -->
  <!-- max 最多缓存的组件实例个数   -->
  <component :is="currentComponentName"> </component>
</keep-alive>

还有一种更新title的方法:

父组件
<!-- <component :title="title" @handle-title="handleTitle" :is="activeType"></component> -->
<!-- $event类似于传过来的值, -->
<!-- <component :title="title" @update:title="title=$event" :is="activeType"></component> -->
<!-- 上种方式可以简写成如下: -->
<component :title.sync="title" :is="activeType"></component>
子组件
<!-- <button @click="$emit('handle-title','three')">three</button> -->
<!-- 子组件的update:title,在父组件直接简写成了.sync就行 -->
<button @click="$emit('update:title','three')">three</button>

vue 自带的动画和过渡效果

vue 本身自带了一个 transition 组件,使用该组件配合一些样式就可以实现进入 or 离开的过渡或者动画效果

  • v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  • v-enter-to:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  • v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  • v-leave-to:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

  1. 用transition把 动画/过渡 包起来
    <transition appear name="fade">
      <p class="tran-text" v-if="show">Hello</p>
    </transition>
    
  2. transition里写上 name 面,替代上面的 v
    .bounce-leave-active {
      // bounce只的是name名,reverse 是相反的执行
      animation: bounce-in 0.6s reverse;
    }
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.5);
      }
      100% {
        transform: scale(1);
      }
    }
    
  3. appear是一上来就执行一次 动画/过渡,也可以设置自定义动画,其中appear不能省略,没有appear-to-class默认和初始效果一样
    <transition
      appear
      name="fade"
      appear-class="rotate-enter"
      appear-active-class="rotate-active"
      appear-to-class="rotate-to"
    >
    
    .rotate-enter {
      transform: rotate(360deg);
    }
    .rotate-active {
      transition: all 1s;
    }
    .rotate-to {
      transform: rotate(180deg);
    }
    

多个元素过渡

当有相同标签名的元素切换时,需要通过 key attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开。

  • out-in:当前元素先进行过渡,完成之后新元素过渡进入。

    <!-- 当你点击按钮的时候实现的动画效果按钮先渐渐消失然后在渐渐出现  -->
    <!-- 按照之前的过渡效果去设置的话,两个元素会同时执行动画,如果希望一个元素效执行效果,然后另外一个在执行的话需要给 transition 加上 mode 属性 -->
    <transition name="fade1" mode="out-in">
      <button v-if="!state" @click="state=true" key="on">on</button>
      <button v-else @click="state=false" key="off">off</button>
    </transition>
    

上述方法可以重写成如下:

<transition name="fade1" mode="out-in">
    <button @click="state=!state" :key="state">{{state?'off':'on'}}</button>
</transition>  

列表渲染

需要使用 transition-group 组件,会渲染成一个标签,默认是span
有一个插件和axios类似,‘loadsh’,其中有一个方法shuffle,

import _ from "loadsh";
// 一个单击事件,把listNum数组倒换顺序。
 methods: {
    shuffle() {
      this.listNum = _.shuffle(this.listNum);
    }
  }
<transition-group name="list" tag="ul">
  <!-- <ul> -->
  <li v-for="num in listNumber" :key="num">{{num}}</li>
  <!-- </ul> -->
</transition-group>

vue 的路由 vue-router

对于 vue 这种单页面应用,官方提供了 vue-router 库,来实现多页的效果。
如何实现

  • 安装 vue-router 包 插件,依赖
  • 新建页面,一般是在views文件夹下,比如 Home.vue,About.vue
  • 新建 router.js 里面创建路由(src下)
    // 一 。创建页面路由
    // 1. 导入页面组件
    import VueRouter from "vue-router";
    import Vue from "vue";
    import Home from "./views/Home.vue";
    import About from "./views/About.vue";
    Vue.use(VueRouter);
    
    // 2. 根据页面组件创建路由数组
    const routes = [
      // 该数组内的某一项就相当于一个页面,页面一般有两部分构成 1. 页面地址  2.页面对应的页面组件
      // / 的意思代表当前服务器的根目录 我们现在其实就是 http://localhost:8000
      // http://localhost:8000/#/
      // http://localhost:8000/#/about
      { path: "/", component: Home },
      { path: "/about", component: About },
    ];
    
    // 3. 根据路由数组创建出路由实例
    const router = new VueRouter({
      routes,
      // 可以选择性的创建路由模式
      // 分为两种 1. hash(默认的 /#/ )    2. history(需要设置,没有/#/ )
      mode: "history"
    });
    
    // 4. 创建完毕之后,导出路由实例,添加到整个 vue 项目中. 参考 main.js 的写法
    export default router;
    
    // 5. 在main.js中导入 
    import router from "./router";
    new Vue({
      router,
      render: h => h(App)
    }).$mount("#app");
    
    
    <!-- 6. 使用 -->
    <ul>
       <!-- 该组件展示的就是路由(页面) 路由的匹配规则要根据当前页面地址和路由的 path 进行匹配 匹配规则是完全匹配-->
      <!-- router-view 代表路由(页面)内容,使用 router-link(a) 组件进行路由(页面)跳转 -->
      <li><router-link to="/">home</router-link></li>
      <li><router-link to="/about">about</router-link></li>
    </ul>
    <router-view></router-view>
    

快速创建路由方式(前提是 vue 的环境时 vue-cli3.0 以上)

  • 使用 vue ui 安装插件,选择安装 vue-router 的插件
  • 使用命令行工具 执行 vue add vue-router

快速安装之后,项目内就会自带一个路由 demo,直接使用即可

对于 router-view 和 router-link 组件的各种配置参考 vue-router 官方文档

router-link 有个属性 active-class

此link激活时的类名,根据地址匹配,默认是包含匹配,想要精确匹配需要 exact 默认是true
,也有默认的类名:router-link-active

<!-- 详情见上面链接中的api文档 -->
<li>
  <router-link active-class="cc" exact to="/">home</router-link>
</li>
<li>
  <router-link active-class="cc" exact to="/about">about</router-link>
</li>

<style>
  .cc{
    color:blue;
  }
</style>

vue-router 进阶

导航守卫

路由跳转的时候会默认触发的一些函数,帮助开发者更好的实现路由的跳转。
一般写在main.js

详细流程请看文档

  • 全局前置守卫1
    router.beforeEach((to, from, next) => {
      // to 和 from  获取的就是组件内的 $route
      console.log("全局前置守卫");
      next();
      if (to.path === "/" || login) next();
      else alert("未登录,请先登录"), next("/");
      // next 参数是一个函数 该函数的作用就是 通行
    });
    
  • 全局解析守卫2
    // 和全局前置守卫差不多,多数用上面的。
    router.beforeResolve((to, from, next) => {
      console.log("全局解析守卫");
      next();
    });
    
  • 全局后置钩子3
    router.afterEach((to, from) => {
      // 没有next函数
      console.log("全局后置钩子");
    });
    
  • 路由独享守卫4
    // 单个用在路由中,不如组件内的守卫好用
    {
      path: '/',
      name: 'Home',
      component: Home,
      beforeEnter: (to, from, next) => {
        console.log(to);
        console.log(from);
        console.log("home路由独享守卫");
        next()
      }
    },
    
  • 组件内的守卫
    export default {
      name: "About",
      // 进入流程 1 6 2 3 
      // 离开流程 8 1 2 3
      beforeRouteEnter(to, from, next) {
        // 6
        console.log("组件内的路由跳转之前的守卫");
        next();
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
      },
      beforeRouteUpdate(to, from, next) {
        // 7
        console.log("组件内的路由改变但是还是展示的该组件");
        next();
        // 可以访问组件实例 `this`
      },
      beforeRouteLeave(to, from, next) {
        // 8
        console.log("组件内的离开当前路由的守卫");
        next();
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
      },
    };
    

过渡动效

其实就是使用 vue 自带的 transition 组件实现过渡效果

### 数据获取(异步)

  • 组件的生命周期内获取
  • 组件的路由守卫内获取

一般在组件内获取数据

// 在 beforeRouteLeave beforeRouteUpdate就不能拿到vm
 beforeRouteEnter(to, from, next) {
    console.log("组件内的路由跳转之前的守卫");
    setTimeout(() => {
      const newNum = to.query.sort === "hot" ? 1 : 100;
      next((vm) => {
        // vm 指得是组件实例
        console.log(vm);
        vm.setNum(newNum);
        // vm.num = newNum; 还是按照官方提供的写,以上。
      });
    }, 1000);
  },
  methods: {
    setNum(newNum) {
      this.num = newNum;
    },
  },

滚动行为

就是在创建路由实例的时候添加一个 scrollBehavior 方法。
默认的滚动行为是回到之前的位置

const router = new VueRouter({
  routes,
  mode: "history",
  scrollBehavior(to, from, savedPosition) {
    // 当按下浏览器的前进后退按钮 savePoition 就存在,或者使用 路由的 back  go 等方法也可以
    if (savedPosition) {
      return savedPosition;
    } else {
      // 没按下前进后退直接滚动到顶部
      return { x: 0, y: 0 };
    }
  }
});
methods: {
  back() {
    this.$router.back()
  }
}

vue 的常用 ui 库,element-ui

普通的

安装方式

  • 安装 npm i element
  • 使用
    • 完全引入(不推荐),直接将 element-ui 和他的 css 导入到 main.js,并且使用 Vue.use 方法将 element-ui 的所有组件,创建成项目全局的组件
    • 按需引入
      • 安装 babel-plugin-component 插件,辅助按需引入
      • 在项目根目录下的 .babel.config.js 添加一项 plugins 配置
      • 在 main.js 中导入所需要的组件,并使用 Vue.use 方法组件创建成项目全局的组件

Message 插件有两种导入方法

全局导入

// 在main.js中全局导入 
import { Message } from 'element-ui';
// 将 Message 方法 添加到整个 vue 的原型对象内,也就是整个项目内都可以使用  this.$message 访问
Vue.prototype.$message = Message

// 在App内用this使用
open2() {
  this.$message({
    message: "恭喜你,这是一条成功消息",
    type: "success",
  });
},

单独导入

// 在App内单独导入 ,Message 指得是一个函数
import { Message } from "element-ui";

open2() {
  Message({
    message: "恭喜你,这是一条成功消息",
    type: "success",
  });
},

InfiniteScroll 无限滚动

滚动至底部时,加载更多数据。使用时如果是按需引用,需要导入 InfiniteScroll(复制粘贴·)

import {InfiniteScroll } from 'element-ui'
Vue.use(InfiniteScroll)
<ul
  class="infinite-list list"
  v-infinite-scroll="load"
  style="overflow: auto"
  :infinite-scroll-disabled="count >= 50"
>
  <li v-for="i in count" class="infinite-list-item" :key="i">{{ i }}</li>
</ul>

cube-ui

  1. 用插件安装:plugin-cube-ui(按需引用,安装后之后把所有的需要都自动引入进来了)
  2. 命令:vue add cube-ui(自己去看文档吧,实在整不出来笔记!)
    (主要 vue ui 库:pc element-ui iview antd …
    移动端 cube-ui mint-ui …)

vue的style样式 : sass less stylus

  • 当使用了第三方插件库的时候 scoped 属性只会将 插件的组件的最外层标签加上 scoped 设置的属性,那么里面的标签想要使用类名修改样式就不行了,去掉scoped就可以了

scss

/* scss 语法就 css 的扩展语法 */
/* 默认我们先安装的 vue 环境是不支持 scss 语法的需要安装工具 */
/* 需要安装 node-sass 以及 sass-loader 到开发依赖 */
/* 安装好了工具包之后就可以在组件内使用 scss 语法了 这是里$*/ 

// 1. 样式嵌套
// 2. 设置变量
// 3. 运算
// 4. if 或者 for
// 5. 样式的导入
<style lang="scss">

@import '../assets/public.scss';
$active: red;
.scss-demo {
  h3 {
    color: $active;
    width: $width;
    // $width 是在public.css里(样式的导入)
  &:hover h3 {
    color: #000;
    // &代表的是父级
  }
}

less

//  需要安装 less-loader 和 less, 基础用法和 scss 一样,这里用@,less和scss基本上一样
<style lang="less">
@color: red;
.less-demo {
  h3 {
    // color: teal;
    color: @color;
  }
}
</style>

stylus

// 需要安装 stylus stylus-loader  注意 stylus-loader 最近更新了 4 版本但是 vuecli4 能使用 3 版本,所以安装 stylus 的 3 版本才可以
// vscode 安装了 vetur 那么 vue 组件内的 style 写 stylus 样式的时候会自动补齐,不想自动补齐 文件--->  首选项---> 设置--->  搜stylus 关闭格式化
</script>

<style lang='stylus' scoped>
h3 
  color teal
  font-size 20px
</style>

flex 布局方式 (去看文档吧!)

  1. flex 是一种弹性盒子布局方式
  2. 抛弃原来的 float 布局方式,float、clear和vertical-align 禁止使用
  3. flex 核心
  • 容器: 设置了 display:flex; 的元素就是容器
  • 容器的属性: flex-direction flex-wrap flex-flow justify-content align-items align-content
  • 容器的轴线: 分横向和纵向,默认主轴线是横轴
  • 项目: display:flex; 的子元素就是项目,项目在主轴线并不会超出盒子,而且项目按照容器的主轴线排列,项目会自动转化为块元素,项目如果没有设置副轴高度默认和容器的高度一致(如果按列排,高就不会超出盒子;如果按行排,宽就不会超出盒子)
  • 项目的属性: order flex-grow flex-shrink flex-basia flex align-self

vue使用swiper

  1. 运行依赖里安装,swiper
  2. 引js,和css,直接在需要用swiper的组件里引就行
// 直接安装 6 版本的swiper会出问题建议安装 5 版本
import Swiper from 'swiper'
// 当你想要引入 包 内的其他文件,路径直接写第三方包名 + / 找即可
import 'swiper/css/swiper.min.css'
  1. 进入页面的时候只有在该生命周期才能获取真实 dom 节点才可以执行一些相关操作,所以js的内容写在mounted里

echarts

  1. 安装运行依赖,echarts
  2. 引入一些需要的模块
// 引入 ECharts 主模块,因为下面需要用echarts对象所以命名了,引入的都是功能,不需要命名,引入了就行了
import echarts from 'echarts/lib/echarts'
// 引入柱状图
import 'echarts/lib/chart/bar'
// 引入提示框和标题组件
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/title'
// 引入提示的方框
import 'echarts/lib/component/legend'

// 详情见vue-native-demo

vuex

Vuex 状态管理模式

就是实现组件之间交互的终极方案。将组件间需要交互的数据(data),共享到 vuex 创建的 store(仓库) 内。

store 的创建

  • 安装 vuex npm i vuex
  • 或者在运行依赖里安装 vuex
  • 在 src 下新建 store.js 文件用来创建 store
    store.js
      // 一. 导入 vuex 和 vue 使用 Vue 的 use 方法将  vuex 做成全局
      import Vue from 'vue'
      import Vuex from 'vuex'
      Vue.use(Vuex)
    
      // 二. 创建 store 仓库,需要使用 Vuex.Store 类(class)  创建
      const store = new Vuex.Store({
        // 相当于定义变量
        state: {
          num: 100
        },
        // 相当于变量的方法
        mutations: {
            update(state) {
                state.num++
            }
        }
      })
      // 三. 将创建好的 store 导出
      export default store
    
  • 在 main.js 内导入 store , 将 store 加入到整个 vue 的项目实例内
    // 四. 在 main 内导入,并且在创建整个 vue 项目的实例的时候添加上 store
    import store from "./store"
    
    new Vue({
      render: h => h(App),
      // store:store
      store
    }).$mount('#app')
    
     // 上述四步完毕之后,vue 项目的所有组件内都可以使用了
    

注意:

 methods: {
    update() {
      this.$store.state.num++;
    },
  },
// 上述操作可以修改 store 内的数据,但是绝对不能使用,就和我们之前说的当 props 是对象的时候子组件不要直接修改,这样修改了之后追踪不到修改的来源
// 想要修改 store 内的数据,必须使用 commit 来触发 mutation 函数,代码如下:
methods: {
    update() {
      // this.$store.commit("update");
      // 也可以写成如下,但前提得引入store.js, import store from "../store";
      store.commit("update");
    },
  },

// payload 为载荷数据

// 组件内:
methods: {
  change() {
    this.$store.commit("change", {
      num1: 10,
      num2: 20,
    });
  },
},
// store.js内
mutations: {
  change(state, payload) {
      state.num = payload.num1 + payload.num2
  }
}
// commit 提交 mutation 可以直接使用 对象形式提交,那么 mutation 函数的第二个参数就会接受整个对象

// 组件内:
change() {
  this.$store.commit({
    // type 代表 mutation 方法名
    type: "change",
    new:{
      num1: 10,
      num2: 20,
    }
    
  });
},
// store.js内:
mutations: {
  change(state, payload) {
    // payload拿到的是整个对象
    state.num = payload.new.num1 + payload.new.num2
  }
}

store 的使用

  • 组件内获取 store 数据
      // 一般获取 store 内的数据写成计算属性的方式
      computed:{
        count: this.$store.state.count
      }
      // vuex 提供了 mapState 辅助函数
      // 就是将 store 内的数据使用该函数映射成组件的 computed
      computed: {
        ...mapState(['count']),
        ...mapState({
          myCont: 'count'
        }),
        ...mapState({
          myCont: state => state.count,
          myCount(state){
            // 这个函数内可以使用 this
            return state.count
          }
        })
      }
    
  • 组件内修改 store 数据
    • 需要在创建 store 的时候定义好修改的方法,也就是创建 mutation
        const store = new Vuex.Store({
          ...
          mutations: {
            add(state){
              state.count++
            }
          }
        })
      
    • 在组件内使用 store.commit('add') 来修改 store 中的数据

mutations

mutation 是函数,这个函数用来修改 store 内的数据的。想要调用这个 mutation 函数的话,必须使用 store 内的 commit 方法。
创建

  const store = new Vuex.Store({
    state: {
      num: 0
      ...
    },
    mutations: {
      // mutation 函数只能接收两个参数
      // mutation 函数默认第一个参数是 state,函数内部直接对 state 内的数据进行修改
      // mutation 函数第二个参数是 payload,修改 state 需要的额外内容,一般写成对象类型
      // mutation 函数必须是同步函数,里面不能加异步操作
      add(state){
        state.num ++
      },
      change(state,payload){
        state.num = payload.newNum
      }
    }
  })

组件内使用

  // 1. 使用 this.$store.commit 去提交 mutation
  this.$store.commit('add')
  this.$store.commit({
    type: 'add'
  })
  this.$store.commit('change',{newNum: 100})
  this.$store.commit({
    type: 'change',
    newNum: 100
  })
  // 2. vuex 提供了 mapMutations 辅助函数
  // 就是将 store 内的 mutation 函数,映射成组件内的 method,并且内部自带 commit 功能

  import  {mapMutations } from 'vuex'

  export default {
    // ...
    methods:{
      // 1. 单击事件里的名字和mutation里的名字相同
      @click="CHANGE({ numData: 500 })"
      ...mapMutations(['CHANGE']),
      // 2. 单击事件里的名字和mutation里的名字不同
      @click="update({ numData: 500 })"
      ...mapMutations({
        jia: 'add',
        update: 'change'
      })
    },
    // 如果 methods 内没有其他的方法可以写成下面的方式
    methods: mapMutations({
        jia: 'add',
        update: 'change'
      })
  }  

  // 3. vuex 提供了 mapState 辅助函数,和mapMutations差不多
  // mapState 辅助函数帮助我们生成计算属性
  computed: {
    // num() {
    //   return this.$store.state.num;
    // },
    // arr() {
    //   return this.$store.state.arr;
    // },
    // 1. 数组用法:
    // ...mapState(["num", "arr"]),
    // 2.对象用法第一种
    // ...mapState({
    //   number: "num",
    //   array: "arr",
    // }),
    // 3. 对象用法第二种
    // 箭头函数获取不到this,所以用普通函数
    // ...mapState({
    //   number: (state) => {state.num},
    //   array: (state) => state.arr,
    // }),
    ...mapState({
      number(state) {
        return state.num + this.a;
      },
      array: "arr",
    }),
  },

getters

就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

getters: {
    // getters 内的函数的第一个参数默认接收 state 作为参数
    // getters 内的函数的第二个参数是 store 的所有 getters 
    // 下面的 sum 是求和计算属性
    sum(state,getters){
      return state.arr.reduce((res,ele)=> res +=ele,0)
    },
    // 下面的 getNum 是获取对应的某个 num , 跟给定的值最接近的那个数
    // 做此功能需要组件内传递限定值
    // 但是默认 getters 的函数是不能接收组件内传递参数的,想要传递话,需要将 getters 函数写成 返回一个函数的函数 ,再返回的函数内接收参数,而且返回的函数的返回值是最终的计算属性
    getNum(state){
      // 20    [11,22,30]      绝对值  Math.abs(x)
      return (limitNum)=> {
        const arr = state.arr
        let res = arr[0]
        if(!(res - limitNum === 0)){
          for (let i = 1; i < arr.length; i++) {
            if(Math.abs(arr[i]- limitNum) < Math.abs(res - limitNum) ){
              res = arr[i]
              if(arr[i] - limitNum === 0){
                break
              }
            }
          }
        }
        return res
      }
    }
  }
  // 触发getters
  limitNum() {
    return this.$store.getters.getNum(30)
  },
  ...mapGetters(["sum", "getNum"]),
  // <div> {{ getNum(30) }}</div>
  ...mapGetters({
    res: 'sum',
    limitNum: 'getNum'
  })

actions

Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态; Action 可以包含任意异步操作。

  // 只要有 action 函数就会有对应的 mutation 函数,所以名称一般写成重名的
  // action 函数默认接受一个 context 作为参数 context 是一个对象,对象下有 commit 方法 以及 store 内的 state
  // 当没有异步操作的时候也可以设置 action 函数
 mutations:{
  updateArr(state,payload){
    console.log('要修改 arr');
    //mutation 函数只能是同步函数不能包含异步操作,需要actions
    // setTimeout(() => {
    //   state.getArr = [1,2,3]
    // }, 1000);
    state.arr = payload.res
  },
}
actions:{
  updateArr({commit},payload){
    console.log(payload.id);
    setTimeout(() => {
      // 用commit触发mutations
      commit({type: 'updateArr',res: [11,22,30]})
    }, 2000);
  }
},
created(){
  this.$store.dispatch('updateArr')
  // 触发 action 也可以写成
  this.$store.dispatch({
    type: 'updateArr',
    // 随便测试了个参数  id
    id: 1
  })
},
methods: {
  // 只能映射成 methods 才带 commit 功能
  // 其他地方想要使用 使用 this 获取
  ...mapMutations(['updateArr'])
}

使用常量替代 Mutation 事件类型(随意用不用)

  // 把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

  // 1. 先建一个 mutation-types.js,里面导出你的方法
  // mutation-types.js内
  // export const ADD = 'ADD'
  // export const CHANGE = 'CHANGE'
  const ADD = 'ADD'
  const CHANGE = 'CHANGE'
  export { ADD }
  export { CHANGE }

  // 2. 在store.js里导入
  // store.js
  import { ADD, CHANGE } from "./mutation-type"

  mutations: {
    // 因为ADD是字符串,使字符串变成函数名或变量加上中括号
      [ADD](state) {
          state.num++
      },
      [CHANGE](state, payload) {
          state.num = payload.new.num1 + payload.new.num2
      }
  }

  // 3. 在组件中也要导入
  // 组件中:
  import { ADD, CHANGE } from "../mutation-type";

  methods: {
    update() {
      store.commit(ADD);
    },
    change() {
      this.$store.commit({
        type: CHANGE,
        newNum: 1000,
      });
    },
  },

vuex 插件

Vuex 自带一个日志插件用于一般的调试,就是当 store 数据发生改变的时候,自动在浏览器中打印详情

// 先导入插件,然后在下面使用
import createLogger from 'vuex/dist/logger'

const store = new Vuex.Store({
  ...
  plugins: [createLogger()]
  ...
})

严格模式

export default new Vuex.Store({
  // 当触发开发环境下执行 vuex 的严格模式
  // 严格模式下的state数据必须通过mutation去修改
  strict: process.env.NODE_ENV !== 'production',
})

表单处理

当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手,可以使用以下方式解决此类问题。

  <input type="text" v-model="objName" />

  computed: {
    // 为什么计算属性不写成 obj 而是写成 objName
    // 因为直接写成 obj 的话,当输入框的值修改的时候,修改的是 obj.name ,obj地址 并不会发生改变,那么 set 函数并不会触发
    objName: {
      get() {
        // get内获取到值
        return this.$store.state.obj.name
      },
      set(newValue) {
        // set 内修改值,修改 store 内 obj,需要调用 mutation
        this.$store.commit('changeObjName', newValue)
      }
    }
  }
}
 mutations: {
    changeObjName(state,newName){
      state.obj.name = newName
    }
  },

module命名空间

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。只有 action 是注册在全局命名空间的。

// 1.  新建一个模块 a    (a.js),
const a = {
  namespaced: true,
  state: ()=>({
    number: 100
  }),
  mutations: {
      changeNum(state, newNum) {
          state.num = newNum
      }
  },
  actions: {
    // changeNum({ commit }, newNum) {
    //     commit('changeNum', newNum)
    // }
    // 以下设置成全局的action
    changeNum: {
        root: true,
        handler({ commit }, newNum) {
            commit('changeNum', newNum)
        }
    }
  },
  getters: {
      aa() {
          return 555
      }
  }
}
// 导出 a 
export default a
// 2. 在总的store模块内导入
import a from './modules/a'
// 3. 在总的store模块内写上模块
export default new Vuex.Store({
  ...
  modules: {
    a,
    b
  }
  ...
})
<!-- 4. 使用 -->
<div>展示带命名空间的 state: {{ num }}</div>
<button @click="changeNum1(500)">直接通过模块内的 mutation 修改</button>
<button @click="changeNum(1000)">直接通过模块内的 action 触发 mutation 修改</button>
<div>{{ aa }}</div>
 computed: {
    ...mapState({
      // num: (state) => {
      //   return state.a.number
      // }
      num: (state) => state.a.num,
    }),
    // ...mapGetters({
    //   aa: "a/aa",
    // }),
    ...mapGetters("a", {
      aa: "aa",
    }),
  },
  methods: {
    // ...mapMutations(['a/changeNum])
    // 以上是错误示范,mutation也并不是全局
    // ...mapMutations({
    //   changeNum1: "a/changeNum",
    // }),
    ...mapMutations('a', {
      changeNum1: 'changeNum'
    }),

    // ...mapActions('a', {
    //   changeNum2: 'changeNum'
    // }),
    // 全局的action不需要,加模块a
    ...mapActions(['changeNum'])
  }

### async await
await 关键词作用是将后面的异步可以看做同步,就是等待异步执行之后在赋值,而且后续操作都会在之后执行,await 后面需要跟着 promise

// getPosts 同下面的 getComments 的作用是一样的
async getPosts ({ commit }) {
  console.log(222)
  const res = await axios.get('http://localhost:3008/post')
  console.log(333)
  commit('getPosts', res.data) 
},
getComments ({ commit }) {
  axios.get('http://localhost:3008/comments').then(res => {
    commit('getComments', res.data)
  })
}
async created(){
  // getPosts提前已经在js里拿到了,这里直接用即可
  // dispatch 执行完毕之后会返回一个promise
  await this.getPosts()
  console.log(111)
  // 输出顺序是  222  111  333  ,为什么自己去理解!
}

vue

vue 的进阶阶段

mixin混入

局部混入

// 1. 先新建一个想要混入的js然后导出
const helloMixin = {
  // 该对象内的格式和组件的导出对象格式一样
  // 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”
  // 恰当的方式
  // 1. data  以组件本身为主
  // 2. 生命周期函数 自动合并(先执行自己的,再执行混入的)
  // 3. methods、components 和 directives 以组件本身为主
  data () {
    return {
      num: 100
    }
  },
  created () {
    console.log('mixin 内的 created hello');
}
// 2. 导出
export default helloMixin
// 3. 在你想要用的组件下导入 ,并设置 mixins 属性就可以使用了
import helloMixin from "../mixins/hello";

export default {
  name: "MixinDemo",
  mixins: [helloMixin],
};

全局混入

// main.js内,或者在其实js里使用
// 全局注册一个 mixin ,所有的组件自动合并该 minxin  慎用
Vue.mixin({
  created: function () {
    console.log('全局混入');
  }
})

自定义指令

v-html
<div v-html="htmlStr"></div> 
  data() {
    return {
      htmlStr: '<span>123</span>'
    }
  },
directive 自定义指令
  <input v-focus:haha.a='666' type="text">
  <br>
  <button v-my-click.once='handleClick'  >测试自定义的点击事件指令</button>

  <script>
    export default {
      methods: {
        handleClick() {
          console.log('事件触发了');
        }
      },
    };
  </script>
  // 在main.js下导入 
  import './directives/index.js'

  // 注册一个全局自定义指令 `v-focus`,在js文件下写
  Vue.directive('focus', {
    // directive 方法的第二个参数会接受一个对象,对象内有一些钩子函数,用来设置指令的功能,钩子函数会在特定的情况下自动触发
    // 常用的两个钩子函数 
    // 1. bind 指令绑定到元素上自动触发,而且只触发一次,此钩子函数执行的时候,原生 dom 节点还没有显示在页面中
    // 2. inserted 当被绑定的元素插入到 DOM 中时

    inserted: function (el, binding) {
      // 下面这个name是我写的,前面可以写值也可以不写值
      // if (binding.name) {
      //     el.focus()
      // }
      if (binding.value) {
        el.focus()
      }
    },
    unbind(){
      // 当此组件销毁的时候执行此钩子
    }
  }),

  Vue.directive('my-click', {
    inserted: function (el, binding) {
      // if (binding.value) {
      //     let count = 0
      //     el.onclick = function () {
      //         count++
      //         if (binding.modifiers.once) {
      //             if (count === 1) {
      //                 binding.value()
      //             }
      //         } else {
      //             binding.value()
      //         }
      //     }
      // }
    const fun = function () {
        binding.value()
        if (binding.modifiers.once) {
            el.removeEventListener('click', fun)
        }
    }
    // remove 删的必须是 add 添加的
    if (binding.value) el.addEventListener('click', fun)
  }
})

  // 注册一个局部的自定义指令,在组件内部写
  directives: {
    focus: {
      // 指令的定义
      inserted: function (el) {
        el.focus()
      }
    }
  }
filter 过滤器

当全局过滤器和局部过滤器重名时,会采用局部过滤器。

<!-- 过滤器的使用  需要写在模版语法中   {{ 参数 | 过滤器名称() }} -->
<span>时间: {{ showTime | formatTime(10,20)}}</span>

<script>
  data(){
    return {
      time: '2019-11-03T00:15:02.362Z'
    }
  },
  computed: {
    showTime() {
      // 当前的计算属性可以实现该组件内的时间格式化,但是其他组件想要使用的话还得再设置
      // 要实现全局功能可能想,将该方法写到一个 公共 js 内哪里使用哪里引入
      // 还可以将该功能写成 vue 的全局过滤器
      return moment(this.time).format('MM-DD HH:mm')
    }
  },

  // 局部过滤器(组件内)
  filters: {
    formatTime: function (value,a,b) {
      // value 指得是time
      if (!value) return ''
      return moment(value).format('MM-DD HH:mm')
    }
  }
 
</script>
// 和 directive一样,先在main.js里导入 
import './filters/index.js'
//全局过滤器(新建的js里)
import Vue from 'vue'
import moment from 'moment'
Vue.filter('formatTime', function (value, a, b) {
  console.log(a);
  console.log(b);
  if (!value) return ''
  return moment(value).format('MM-DD HH:mm')
}

vue 插件

插件通常用来为 Vue 添加全局功能。

// 组件内,输出下面定义的axios,测试
created(){
  console.log($http)
  console.log($xx)
}
import UseDemo from './components/UseDemo.vue'

// vue 的插件功能 , 其实就是给组件添加全局功能
// 1. 使用 Vue.prototype 向 vue 的实例内添加属性或方法
Vue.prototype.$http = 'axios'
// 2. 通过 Vue.use 方法   Vue.use(MyPlugin)
// 这种方法比较好,它会自动做检测功能(我也不太懂!) 
const MyPlugin = {
  install: function (Vue, a, b, c) {
    Vue.prototype.$xx = '测试'
    // 将 UseDemo 组件注册成全局组件,标签名是 UseDemo,直接使用即可,不需要引用和注册,前提是得把这个组件引用到现在的js里,在上面第一句
    // 前面是标签名,后面是组件名
    Vue.component('UseDemo', UseDemo)
    console.log(a, b, c);

  }
}
// 可以传参 1 2 3
Vue.use(MyPlugin, 1, 2, 3)
// Vue.use(MyPlugin) 会自动执行 MyPlugin 内的 install 方法,use 方法会自动检测全局的你的插件是否注册没注册

渲染函数 & JSX

创建函数式组件 两种方法 :

  1. Vue.component 方法创建函数组件,这种创建出来的组件是全局组件

    <!--  第一种引用注册再使用,第二种直接使用即可-->
    <Vuetitle :level="1">hello 函数组件</Vuetitle>
    
    // 第一种方法 全局组件 (js格式的)
    const Title = Vue.component('Vuetitle', {
      // render必须返回一个vnode节点(用createElement创建的),或者html节点,但是在全局组件中只能返回 vnode (可以是一个组件,也可以是 h 创建的) 不能返回 html 标签,这里不支持 jsx 语法, 局部组件里都可以创建
        render: function (createElement) {
            // createElement 就是创建虚拟dom节点(vnode),或取名 h
            return createElement(
                // 标签名称
                `h${this.level}`,
                // 标签里的文字,传过来的是文本节点,底层是一个对象
                this.$slots.default
            )
        },
        props: {
            level: {
                type: Number,
                default: 1
            }
        }
    })
    // 导出Title,
    export default Title
    
    // 还可以在main.js里直接导入
    import "./components/title"
    // 但组件得写成以下形式,app里直接使用即可
    Vue.component('Vuetitle', {
      render: function (createElement) {
          return createElement(
              `h${this.level}`,
              this.$slots.default
          )
      },
      props: {
          level: {
              type: Number,
              default: 1
          }
      }
    })
    

上面标签里的文字因为没起名,所以默认名字是default,想要命名的话用以下方法,名字为text :

<Vuetitle :level="1">
<!-- 或者写成 #text -->
  <template v-slot:text>
    <a href="#">hello 函数组件</a>
  </template>
</Vuetitle>
// 如下使用方法
this.$slots.text

如果想在传过来的内容里添加标签可以如下设置:

// 这时h4里面有两个标签  a  span
// 第一个参数是标签名,第二个是对象,里面设置一些属性,第三个是默认的文字
  return createElement(
      'h4',
      [
          createElement('a', {
              attrs: {
                  href: '#'
              }
          }, this.$slots.default),
          createElement('span', {
              style: {
                  cursor: 'pointer',
                  color: 'red'
              }
          }, this.$slots.default)
      ]
  )

想要添加一些事件,比如给span添加,可以如下设置:

render: function (createElement) {
  return createElement(
      'h5,
      [
        createElement('span', {
            on: {
                click: this.test
            }
        }, this.$slots.default)
      ]
  )
},
methods: {
  test() {
      console.log('测试');
  }
}, 
// 或者把事件写上面
on: {
  click(){
    console.log('测试')
  }
}
  1. 直接在 js 文件内写一个对象,对象内包含一个 render 方法即可,render 方法必须返回 html 节点(局部组件,哪用哪引)

    // 下面的 js 内嵌套了 html 语法,我们称这种写法为 JSX 语法,默认这种语法是不支持的,需要 createElement(h) 支持,现在h可以省略了
    
    export default {
      render(h){
        // return h(`h${this.level}`, {}, '文字内容')
        // 上面是换标签名的,下面换标签名的方法需要定义一个变量
        const tag='h'+this.level
        // return <tag>局部函数组件</tag>
        // 如果想要使用传过来的文字,以下方法:(tag标签里是可以套其它标签的)
        return <tag>{this.$slots.default}</tag>
        
      },
      props: {
        level: {
          type: Number,
          default: 1
        }
      }
    }
    
    // 在想要用的父组件中引用 注册 使用
    import Title1 from "./components/titleComponent1";
    

绑定事件的写法

// return h(`h${this.level}`, { on: { click: this.handleClick } }, '文字内容')
// JSX 语法内想要添加 js 需要先使用 {} 包裹,  onclick也能用
return <h3 on-click={this.handleClick}>局部函数组件</h3>

JSX小知识:

render() {
  // onClick  ||   on-click
  // 想要实现 v-if 的语法是在外面加上{},实现 v-show 的语法是写在 style 里,需要加两个{},因为style里是对象
  // 想要实现 for 循环可以用 map()
  return <div>
            {this.show ? <h1 style={{width:'200px',display:this.show1 ? 'block' : 'none'}} class={this.show ? 'active' :''} onClick={this.clickFun}>{this.title}</h1> : <h1>不存在</h1>}
            <hr />
             <ul>
                 {this.arr.map(item => <li>{item}</li>)}
             </ul>
      </div>
},
data(){
    return {
        show: true,
        show1:true,
        arr:[1,2,3]
    }
},

小例子

在父组件里的方法里自己定义一个render函数,决定要生成什么标签,然后传给最终的js组件,用props,type是Function接收(借助render函数传递自定义的html内容)

<List :arr="['香蕉', '西瓜', '平果']" :render="render" />
// 见vue-advance里的 List组件(苹果香蕉大鸭梨)
// 父组件内定义的render方法
methods: {
  render(h, item) {
    // 下面是 jsx 语法, 默认不支持 因为不是函数组件内部的 render 函数,所以需要函数组件内传递过来 h
    return <h1>{item}</h1>
  }
}

// 函数组件内 (js)
  render(h) {
    // 这里判断的是用户自己写没写,没写默认就是span标签
      return <li>
          {this.render ? this.render(h, this.item) : <span>{this.item}</span>}
      </li>
  },
  props: {
      item: {
          type: String,
          required: true
      },
      render: {
          type: Function,
      },
  }
}

还有一种方法是借助作用域插槽实现列表内容自定义(这种更简单)

<!-- 父组件  -->
<List2 :data="['香蕉1', '西瓜1', '平果1']">
  <template v-slot:a="{ item }">
    <h2>{{ item }}</h2>
  </template>
</List2>

<!-- 子组件 -->
<ul>
  <li v-for="item in data" :key="item"><slot name="a" :item="item" /></li>
</ul>

变成插件的方法

// 1. use方法必须传递个对象,对象下必须有个install方法(插件一般是在install方法下的)
const Title = {
    install: function (Vue) {
        Vue.component('Vuetitle', {
            render: function (createElement) {
                return createElement(
                    `h${this.level}`,
                    // this.$slots.text
                    this.$slots.default
                )
            },
            props: {
                level: {
                    type: Number,
                    default: 1
                }
            }
        })
    }
}
// 2.
export default Title
// 3. 在plugins/Title里
import Vue from "vue"
// Vuetitle 这个名字必须和自己新建的组件名一样
import Vuetitle from "../components/title"
Vue.use(Vuetitle) 

// 3. 在main.js里引入
import "./plugins/Title"

// 或者 没有plugins/Title,就把这里面的内容写进main.js 引用

用 node + express 搭建简易后台服务器

  1. express : node的包,快速搭建服务器,安装express依赖
  2. 在外面(src外)新建一个server.js文件,里面引用:
  // es6语法针对src里的东西,怕用不了
  // import express from "express"
  // 下面是node语法:
  const express = require('express')
  cosnt app = express()
  // /menus是一个接口,需要拼上下面的地址,把对象下的a转换成json再返出去(get方法返回一个json数据)
  app.get('/menus',function(req,res){
    res.json({
      a:[1,2,3]
    })
  })
  app.listen(3000,function(){
    console.log('服务器已经启动,http://localhost:3000/')
  })
  // 以上就起了一个node服务
  1. 运行服务器:node server.js
  2. 在组件里发axios请求来拿数据
const res = await axios.get('http://localhost:3000/menus')
console.log(res.data.menuList)
  1. 跨域小知识:此时会遇到跨域问题,两个不同的服务器进行互动就是跨域,http://localhost:8080,协议 域名 端口,其中一个不一样就叫跨域,比如和 http://localhost:3000 就不一样,怎么解决此问题呢?
// 写在app.get上方
app.all('*', (req, res, next) => {
  // 响应头的设置,我的后台支持跨域请求
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  res.header("Access-Control-Allow-Methods", "*");
  res.header("Content-Type", "application/json;charset=utf-8");
  next();
})
  1. 响应拦截器小知识:对反映出来的数据进行一些处理
axios.defaults.baseURL = "http://localhost:3000";
// 响应拦截器
axios.interceptors.response.use((response) => {
  // axios 就会以这个函数的返回值作为响应的结果
  return response.data;
});

// 上面写了默认基地址,这里就可以直接写后面的地址
const res = await axios.get("/menus");
console.log(res.menuList)
  1. post小知识:解析post传过来的数据
// 传参
await axios.post("/menus", { text });
// 先下载,命令行中:npm i body-parser
// 后台中服务器接收
const bodyParser = require('body-parser');
app.use(bodyParser.json())

app.post('/menus', function (req, res) {
  // 使用 text前要加上 req.body
  console.log(req.body.text)
}

浏览器的本地存储

    <button class="set">存储</button>
    <button class="get">获取</button>
    <button class="del">删除</button>
    <h2>浏览器的本地存储</h2>
    <script>
        // sessionStorage:关闭了网页之后存储的就没有了
        // localStoreage:关闭了网页之后存储的依然在,除非用以下手法手动删除
        document.querySelector('.set').onclick = function () {
            sessionStorage.setItem('password', JSON.stringify({ x: 123456 }))
            // x 自动存的是string,结果是获取不到对象 结果是 [object Object],所以要想取到对象,需要用JSON.stringify()
            sessionStorage.setItem('username', '小张')
        }
        document.querySelector('.get').onclick = function () {
            const pas = sessionStorage.getItem("password")
            // 因为拿到的结果是json字符串,所以需要json.parse做解析才能拿到对象
            console.log(JSON.parse(pas).x);
        }
        document.querySelector('.del').onclick = function () {
            // sessionStorage.removeItem("username")
            sessionStorage.clear()
        }

    </script>

小例子,自己做一个路由插件

具体思路参考 mini-vue-router 小例子,自己创建 router.js
包含的知识:

// new Vue 创建一个vue实例(类),其实就是一个vue组件,这个组件是根组件,是属于vue内所有组件的祖先,也就是其他组件(App..)都会继承该组件 ,new Vue 传递的参数可以使用组件的this.$options获取
// 这里引入了router 那么所有的组件内都多了:
// $router 和 $route,   $router 内指得就是VueRouter类,$route内指得是当前路由里的一些信息(没用)
new Vue({
  router,
  render:h=>h(App)
}).$mount('#app')

// 指得是:VueRouter是一个插件,需要使用install方法
Vue.use(VueRouter)

// 指得是:new VueRouter 意思是VueRouter是一个类(也算是一个实例),这里传参过去,对面需要用constructor 接收这个参数,
const router = new VueRouter({
  mode: 'hash',
  base: process.env.BASE_URL,
  routes
})

// install 方法第一个参数可以接收一个Vue,此 Vue 指得是整个大的Vue组件,这样就可以使用Vue来添加一些公共的东西了
let Vue
VueRouter.install = _Vue =>{
 Vue = _Vue
// 制作插件的功能就是给 vue 组件做一个全局的功能,所以放在这里比较合适
// Vue.prototype.$router = VueRouter
}

解决跨域方法

  • 做代理(将我们本地的localhost:8080让别人去做代理,帮我们请求)
    1. 先新建一个vue.config.js的文件,和package.json同级
    
    // node 的一个导出方式 
    module.exports = {
      // 开发的一个服务器下做个代理
      devServer: {
          // 修改了配置得重启服务
          proxy: {
            '/api': {
                target: 'https://vue-js.com/api/v1',
                ws: true,
                // 需要虚拟的主机地址
                changeOrigin: true,
                // 带着api的请求要发到target里,然后必须重写api
                pathRewrite: {
                    "^/api": ""
                }
              }
          }
      }
    }
    
    // 需要跨域访问的是 https://vue-js.com/api/v1/topics
    // axios.get('api/topics'),遇到api就变target的地址
    // 这样就可以访问想要跨域访问的那个地址了
    // 此代理只对本地服务器生效,如果用node随便打开了一个服务器,此代理是不管用的
    // 代理是vuecli做的,vuecli是搭建vue的环境的一个工具
    

typescript

  1. 先建立一个.ts结尾的文件(index.js)
  2. 再安装 typescript: npm install -g typescript
  3. 然后在命令行中输入命令: tsc index.ts , 自动就多出一个index.js(js文件内把ts解析成js)
  4. 然后在html中导入 js 文件,就可以使用了!
    (想要查看ts文件可以用node:ts-node index.ts 这条命令 = node index.js)
    (想要在页面中执行只要修改了就要重新执行命令:tsc index.ts)
// 布尔
let isDemo: boolean = true
// 以下是出错的
// let isDone1: boolean = new boolean(false)
let isDone1:Boolean = new Boolean(false)

// 数字
// 变量不想赋值成任何的初始值,可以赋值 undefined
let num: number = undefined

// 字符串
let str:string='hello'

// undefined 
// undefined 是所有类型的子类型
let und: undefined = undefined

// null
let nul: null = null

// void 空值
// 只能是null和undefined,定义一个空值没有用,一般在函数的返回值的时候设置
let unusable: void = undefined

// any 任意值
// 可以赋值任意类型的值,没定义默认为any
let anything: any = 'hello'
let something
something = 7
something = 'hello'

// 类型推论 声明变量的时候没有声明类型 如果赋值了 类型推论就会帮助你自动定义类型
let num3 = 10
num3 = 'hello world'
// 此时就会出错,因为num3以为被认为是number了

// 联合类型
// 既可以是数字类型也可以是字符串类型
let number: number | string = 10

// 数字数组
let arr1: (number | string)[] = [3, 4, 5, '6', 7]
// 下面默认是number[]
let arr2 = [1, 2, 3]
// 数组的泛型表示法  Array<元素类型>
let arr: Array<number> = [1, 2, 3]

// 元组 元组类型允许表示一个已知元素数量和类型的数组
let x: [number, string] = [1, '2']
// x[2]=8 会出错,元组已经定义好长度了,不能单加

// 对象类型 Object
// 需要定义一个接口
interface Person {
    name: string,
    // 可选属性 加 '?'
    age?: number,
    hobby: string[],
    // 规定下面所有的属性,若把any换成number或者其它,写了会对之前存在的属性进行校验,会出错!
    [propName: string]: any,
    // 只读属性不能修改
    readonly firstName: string
}
let user: Person = {
    // 默认不能缺少任意一个属性,对应到Person接口(可加 ?)
    name: '花花',
    age: 28,
    hobby: ['1', '2'],
    x: 10,
    y: 'hello',
    firstName: '张'
}

// 函数类型
// 在ts内函数的声明,需要声明参数和返回值的类型
// 函数式的创建
function add(a: number, b: number): number {
    // 如果类型不写,就默认为any
    return a + b
}

// 表达式创建
// 必须设置成变量的类型: 函数类型 (参数类型)=>返回值类型 = 函数
// 类型推论可以省略变量(add1)的函数类型声明
const add1: (a: number, b: number) => number = function (a: number, b: number) {
    return a + b
    // 函数没有返回值,默认返回undefined
}
// 箭头函数
// const add1: (a: number, b: number) => number = (a: number, b: number): number => a + b

// 函数没有返回值,返回值类型设置成 void
function fun(x: string): void {
    console.log(x);
    // void 必须返回 null 或者 undefined 或者不写 return
}

function fun1(x = 'hello', y?: string, ...rest: number[]): void {
  // 如果没传 y 就是 undefined
    console.log(x + y);
    console.log(rest);
    // rest 拿到的就是1-5的数组(剩余参数)
}
// 'world' 必须得传,要不然就没有剩余参数了
fun1('hello', 'world', 1, 2, 3, 4, 5)


// 例子: 创建一个函数,将传递的数字或者字符串进行倒序输出
// 函数重载,解决定义不明确问题:传什么类型,拿什么类型
function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''))
    } else {
        return x.split('').reverse().join('')
    }
}
console.log(reverse('hello'));

// 类型断言:将联合类型断言为其中某一种类型
// 将任意一个类型断言为any类型
(function y() {
    // window.z=10  出错
    (window as any).z = 10
    // window全局对象在ts中默认不能使用,需要定义,只有文件运行在浏览器上才会有window
})()
// 1. 值 as 类型 (常用)
 if (typeof (animal as Fish).swim === 'function')
// 2. <类型>值 
if (typeof (<Fish>animal).swim === 'function')

interface Cat {
    name: string
    // 定义一个run方法,是空值,不是返回空值
    run(): void
}
interface Fish {
    name: string
    swim(): void
}
const cat: Cat = {
    name: '小轩',
    run: function () {
        console.log('跑');
    }
}
function isFish(animal: Cat | Fish): boolean {
    // if (animal.swim) {
    // 在这直接判断animal是否拥有swim属性会报错:ts 编译失败
    // 因为Cat没有此属性 
    // 需要使用类型断言,先断言animal是Fish接口类型
    // 判断是不是函数更为严谨
    // if (typeof (animal as Fish).swim === 'function') {
    if (typeof (<Fish>animal).swim === 'function') {
        return true
    }
    return false
}
console.log(isFish(cat)); // false


// 内置对象
const date = new Date()
const re = new RegExp('[abc]')

// DOM  document
console.log(document);
// 单一元素的类型 HTMLElement
const box: HTMLElement = document.querySelector('.box')
// 元素集合类型 NodeList
const box1: NodeList = document.querySelectorAll('.box')
// MouseEvent鼠标事件对象的类型
box.onclick = function (e: MouseEvent): void {
    console.log(e);
}
box.addEventListener('click', function (e) {
    console.log(e.target);
}),

// BOM  window (上面需要加逗号)
// window.z = 88  出错,必须类型断言
(window as any).z = 88
console.log((any<window>).z);

// 类型别名
// 使用 type 创建任意的类型别名
type Num1 = number | string
const num1: Num1 = 10
type numArr = number[]
const numArr1: numArr = [1, 2, 3]

// 自面量类型
// 只能赋值其中之一
type Fruit = 'apple' | 'orange' | 'pear'
const fruit: Fruit = 'apple'

// 玫举类型
// 一般存储的是常量,第一个如果赋值为数字,那后面的数依次类推,默认就为0
// 如果前面的赋值为其它,那后面的必须也赋值,否则出错
// log玫举类型,是 {'0':'Sun', Sun:0}
enum Days { Sun = 0, Mon, Tue, Web, Thu, Fri, Sat }
// const day: Days = Days['Tue']
// console.log(day);   2
// console.log(Days[2]);  Tue
const day: string = 'Mon'
if (Days[day] === 1) {
    console.log('周一');
}

// 类
class Person1 {
    // 类内的属性和方法需要先定义好类型
    // 然后constructor内才能给这些属性或者方法赋值
    // public name:string 简写如下:
    // name: string
    age: number
    // 也可以在里面声明
    constructor(public name: string, age: number) {
        this.name = name
        this.age = age
    }
    say() {
        console.log(this.name);
    }
}
// 1. const user1: Person1 = new Person1('小张', 2)
// 可以写成以下:2.
const user1: Person1 = {
    name: '小红',
    age: 2,
    say() {
        console.log('888');
    }
    // 必须写say(),否则会报错
}
user1.say()
// 这里执行的是888,覆盖住了1里的say,所以2不如1好

// 继承
class Animal {
    name: string
    constructor(name: string) {
        this.name = name
    }
    sayHi() {
        console.log(`my name is ${this.name}`);
    }
}
class Fish extends Animal {
    constructor(name: string) {
        super(name)
    }
}
const fish = new Fish('小小')
console.log(fish.name);  // 小小
fish.sayHi() // my name is 小小
// log(fish) :   Fish {name: "小小"}   
// Fish 的_proto_是 Animal,animal下有constructor,还有_proto_是sayHi

// ts 里面可以对类使用几个修饰符(属性,方法)
// public 默认,能改能用
// private 只能在类中访问,别处只能看不能用
// readonly 只读,能访问不能修改
// protected 子类可以访问

// 类里面的存取器,属性和获取和修改
class People {
    constructor(name: string) {
        this.name = name
    }
    get name() {
        return ''
    }
    set name(value) {
        console.log(value);
    }
}
const beauty:People = new People ('小张')
beauty.name='小峰'
// 小张    小峰
// name 一修改就触发 set

// 泛型 
// 创建一个任意长度的数组,而且创建出来的数组每一项是有默认值的
function createArray<T>(length: number, value: T): Array<T> {
    let res: T[] = []
    for (let i = 0; i < length; i++) {
        res.push(value)
    }
    return res
}
const res = createArray<string>(4, 'x')
// 按照之前的写法,res就没有确定的类型,现在的做法是为了让res的类型固定些
// const res = createArray(4, 'x')
// 类型推论可以省略函数传递的泛型,也就是把下面的string省略掉

// 声明文件
// 我们要使用第三方插件 jquery
// 需要安装 jquery 和 @types/jquery (用来声明jquery的),导入方式换成下面这种,
import $ = require('jquery')
console.log($);  // Function

在 vue2.x 中使用 ts

格式化函数时,函数名称或function关键字与开始参数之间会自动没有空格。想要让它有空格,需要在 .gitignore 里 加上一句话:space-before-function-paren = 0

安装 Vue TypeScript Snippets 0.1.3 插件,就可以使用 v-ts-c

导入子组件、methods、data:

import ButtonDemo from '../components/ButtonDemo.vue'
// 必须加后缀名 .vue
import { Component, Vue } from 'vue-property-decorator'
// 我们通过 vue-property-decorator 导入了 Vue 以及 Component
//  vue-property-decorator 是一个vue类组件装饰器 快速方便的使用类组件内的属性和方法

// @Component 装饰器方法:不可省
// 作用1:可以导入子组件
// 作用2:声明生命周期
// 作用3:设置计算属性
@Component({
  components: {
    ButtonDemo
  }
})

export default class Home extends Vue {
  // data 和 methods 直接写在类里
  count = 0
  add() {
    this.count++
  }
  sub() {
    this.count--
  }
}

子组件内:Prop、Emit

// 父组件:
<ButtonDemo @add1="add" text="默认按钮"></ButtonDemo>

// 1. 子组件:对应不换名的
<button @click="add1">{{ text }}</button>

// 2. 子组件:对应换名的
<button @click="plus">{{ text }}</button>

export default class ButtonDemo extends Vue {
  // Prop 装饰器可以获取父组件传递的props
  // 需要在vue-property-decorator里导入Prop
  // Prop()里的String可写可不写,尽量写,写上就是校验
  // 1. 不带校验的写法,可以写在一行
  // @Prop(String)
  // text: string
  // 2. 带校验的写法
  @Prop({
    type: String,
    default: '按钮'
  })
  // prop 有时会提示错误:你的prop没有赋值(空值)
  // 1. 直接在 text 后面加一个 !    2. 给text 声明一个联合类型  string | undefined
  text!: string

  // 自定义事件,接收事件
  // 两个事件(自动合并),先执行子组件的再执行父组件的
  // 1. 不换名的自定义事件方法
  @Emit()
  add1(){
    console.log('子组件的 add1')
  }
  // 2. 换名的自定义事件方法
  @Emit('add1')
  plus() {}
}

生命周期、计算属性、侦听器:

<div>
  <router-link to="/?tab=all">去全部</router-link> |
  <router-link to="/?tab=ask">去问答</router-link>
</div>
// 1. 生命周期写在Component里
@Component({
  mounted() {
    console.log('子组件的mounted')
  },
  // 1. 计算属性写在Component里的computed里
  computed: {
    num() {
      return 666
    }
  }
})

export default class ButtonDemo extends Vue {
  // 2. 生命周期写在类里
  created() {
    console.log('子组件的created')
  }

  @Watch('$route.query.tab',{ immediate: true })
  onTabChange(newValue: string, oldValue: string) {
    console.log(newValue, oldValue)
  }

  // 2. 计算属性写在类里,用 get(可以定义返回值类型)
  get fullName(): String {
    return 'name'
  }
  set fullName(newValue: String) {
    console.log('fullname 修改了')
  }
}

路由( $route, $router )的小知识:

import { Route } from "vue-router"
export default class Home extends Vue {
  // 一般情况需要定义类型,可以给上面导入的 Route 类型
  // 可以省略类型(老师建议)
  $route: Route
  test() {
    // 这里 $route 没有定义 可以使用,但提示错误 
    // 需要在上面定义一下
    console.log(this.$route.query)
  }
}

state: 拿store里的state

export default class Count extends Vue {
// 1. 需要先安装运行依赖:vuex-class
// 2. 导入:import { State } from 'vuex-class'
// 3. store 中取 str,给str1
// 注意:这种只适用于state里,模块里的不能用
  @State('str') 
  str1!: string
}

PropSync

// 父组件内:
<Count :count.sync="count" />

// 子组件内:显示父组件的count
<div> {{ cnt }} </div>
<button @click="changeCount(2200)">修改父组件传递的cnt</button>

export default class Count extends Vue {
  @PropSync('count', { type: Number })
  // cnt 和 count 不能重名
  cnt!: number

  // 1. 修改 cnt (以前的方法)
  changeCount() {
    this.$emit('update:count', 200)
  }
  // 2. 修改 cnt (现在的方法)
  @Emit('update:count')
  changeCount(num: number) {}
}

Model

// 父组件内:
<Count v-model="text" />

// 子组件内:
// 1. 第一种修改方法
<input type="text" :value='value1' @change="$emit('changeText',$event.target.value)">

// 2. 第二种修改方法
<input type="text" :value="value1" @input="changeText($event.target.value)" />

export default class Count extends Vue {
  // 第一个参数是传递的事件,默认input,(给了一个自变量input1,上面使用的是input1),第二个是传递的变量的类型(这里是text也就是value的类型)
  // 装饰器装饰的就是传递过来的 props 也就是 value1,value1是给传过来的value换了个名字,用value也行
  // 1. 第一种方法 
  @Model('changeText', { type: String })
  value1!: string
  
  // 2.第二种方法:个人认为第二种方法多余!
  @Model('input', { type: String })
  value1!: string
  @Emit('input')
  changeText(str: string) {}
}

(state) store: vue 内 ts 写法

// 1. 需要安装运行依赖 vuex-module-decorators,借助它去创建 store 的模块
// 2. 导入: VuexModule: 是用来创建模块的类,这个类需要 Module 装饰器 
import { VuexModule, Module } from "vuex-module-decorators"
// 3. 创建一个类继承 VuexModule
export default class Company extends VuexModule {
    // state 
    companyName = '第嘉'
}
// 4. 在路由的index里导入,写进模块中,就能使用了
import company from "./modules/company"
export default new Vuex.Store({
    modules: {
      company
    }
})
// 5. 使用
this.$store.state.company.companyName

@Module 装饰器内直接将这个模块添加到 store 内,不需要在创建 store 的时候导入模块,再添加到 modules 内 的方法。

模块(company.ts)内:

interface companyInfo {
  name: string,
    created_at: string,
    info: string
}
// 如果有接口,可以把总接口也导出,给到下面company的类型
export interface ICompany {
    companyInfo:companyInfo
    arr:Array<number>
}

// 1. 在模块的文件下先导入 store 下的 index:  `import store from "../index"`
// 2. 在装饰器内 写上store 并且添加 dynamic 属性 属性值为 true
@Module({
    name: 'company',
    store,
    // 动态创建模块 将模块自动添加到 store 内
    dynamic: true
})
class Company extends VuexModule {
    companyInfo: companyInfo = {
        name: '第嘉',
        created_at: '2020-12-11',
        info: '前端学习营地'
    }
    arr: Array<number> = [1,2,3]
}
// 4. 导出模块给组件使用
export const CompanyStore = getModule(Company)

store 的 index.ts 内:

// 如果company.ts 下有导出的接口,可以先导入接口
import { ICompany } from "./modules/company"
// 再把下面company的类型换成: company: ICompany

// 1. 写个接口 IRootState,接口名任意
interface IRootState {
  company: any
}
// 2. 把 IRootState 写进 Store 内
export default new Vuex.Store<IRootState>({})

company.vue内:

// 先导入模块内导出的CompanyStore
import { CompanyStore } from '../store/modules/company'
// 使用 CompanyStore 内是所有的state
console.log(CompanyStore)
console.log(this.$store.state.company) 
// 有 companyInfo 对象和 companyName 字符

Mutation,get,Action

// 假装给action做的异步请求
const getList = () => new Promise<Array<number>>(resolve => {
    setTimeout(() => {
        resolve([1, 2, 3, 4, 5, 6, 7])
    }, 1000);
})

class Company extends VuexModule {
    arr: Array<number> = []

    // Mutation Action 在上面先导入
    @Mutation
    getArr(payload: Array<number>) {
        this.arr = payload
    }

    get len() {
        return this.arr.length
    }

    @Action
    async fetchArr() {
        const newArr = await getList()
        this.getArr(newArr)
    }
}
// 在组件内使用:
export default class Company extends Vue {
  get arr() {
    return CompanyStore.arr
  }
  created(){
    // CompanyStore.getArr([1,2,3])
    CompanyStore.fetchArr([1,2,3])
  }
}

webpack(webpack-demo)

参考网址https://v4.webpack.docschina.org/guides/getting-started/

基本安装和打包编译和配置按着网址写即可(npx webpack, 自动生成 main.js, 每修改每编译)

配置项:需要在根目录下新建 ‘webpack.config.js’,内容如下
const path = require('path');
module.exports = {
  // 入口,打包编译哪个文件(其它我们打包的是main.js)
  entry: './src/index.js',
  // 出口设置,编译完成之后的文件
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
解决每修改每编译问题:开发环境->webpack-dev-server
// 1. (按着步骤往下走)
// 先安装:npm install --save-dev webpack-dev-server,再粘贴
// 2. 开发服务器 监听的文件内容
// a. dist 下的内容起在了服务器 http://localhost:8080 下
// b. 当入口文件被修改的时候,会自动重新编译并且重新载入新编译之后的文件
devServer: {
  contentBase: './dist'
},

"serve": "webpack-dev-server --open"
// 此时不能用,因为 webpack-dev-server(3) 和 webpack-cli(4) 的版本不一样,应降低一版本
npm install webpack@4 webpack-cli@3 --save-dev
// 运行:npm run serve(此时页面就被打开,修改就不用编译了)
模块热替换:指南 -> 模块热替换 -> 启用 HMR (webpack4按步骤走即可,写在webpack.dev.js里 ,共三步)
管理资源->加载 css (按步骤走即可)
// 直接将css文件当做了模块导入到 index.js 内
// 默认只有 js 文件才会被当做模块处理
// 所以需要处理非 js 模块类型的文件,需要 loader
// 比如处理 css ,需要安装 css 对应的loader
import "./assets/style.css"
管理资源->加载 images 图像 (按步骤走即可)
import pic from './assets/logo.png'
// 需要安装loader,只有使用变量的时候才可以
element.innerHTML = `<img src='${pic}' />`
开发环境->使用 source map (按步骤走即可)
// 点击可用选项 即可以查看更多,老师给选的是 eval-source-map
// 功能是报错行数显示的更加准确
devtool: 'inline-source-map',
管理输出->设置 HtmlWebpackPlugin (按步骤走即可)
  // 没有模板:
  plugins: [
    // 应用上 HtmlWebpackPlugin 插件功能,自动创建 html 文件,你可以在使用插件的时候,传递一些页面的配置,此时可以删除 index.html
    // 页面是自动创建的,里面基本上什么都没有,如果你想要以一个Html模板创建index.html,可以添加html-webpack-template属性设置
    new HtmlWebpackPlugin({
      title: '我的 webpack-demo'
    })
  ],

  // 有模板
  plugins: [
    new HtmlWebpackPlugin({
      // 此时需要在dist下创建一个模板,名字任意
      template:'./dist/index.html'
    })
  ],
靠拢vue小知识:向 vue 靠拢 (下载 vue ,创建 main.js)
  // npm i vue
  
  import Vue from 'vue'
  new Vue({
      // 创建 vue 的实例人对象的时候必须传递一个render函数,而且该函数必须返回一个dom节点劳动者VNode节点
      // 可以使用 h 参数创建一个 VNode 节点
      // render: h =>h('h1',{},'hello')
      render: h =>h(App)
      // 此时页面都出错,解决方法如下一条
  }).$mount('#app')

LOADER -> vue-loader -> Documentation ->Getting Started (按步骤走即可)
  // 使用 vue-loader, 需要下载 vue-loader之外 还要下载辅助的插件 vue-template-compiler
  // 先 const,rules、plugins里也加东西
  // -D 意思是 --save-dev
  npm install -D vue-loader vue-template-compiler
开发环境 -> 使用 webpack-dev-server -> 配置文档 -> devServer.clientLogLevel
  module.exports = {
    //...
    devServer: {
      // 控制台(console)显示消息:关闭
      clientLogLevel: 'none'
    }
  };
开发环境 -> 使用 webpack-dev-server -> 配置文档 -> devServer.historyApiFallback
  module.exports = {
    //...
    devServer: {
      // 当使用vue路由的时候,选择了history模式,在这个模式下,刷新非首页的页面,直接找不到 404,需要将该页面下的所有页面请求,全部指向到首页
    historyApiFallback: true
    }
  };
loader小知识:想要引入element-ui的时候,需要字体模块的 loader ,如下例子
关于在webpack-demo里创建 element-ui :
// 先安装 npm i element-ui
// 完整引入,在 main.js 中:
import ElementUI from 'element-ui';
// 此时会出错,解决方法是需要 字体模块 的loader
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
// 按需引入
// 1. 先安装 npm install babel-plugin-component -D
// 2. 在根目录下新建 .babelrc 文件,内容见 element 官方文档(快速上手),其中 es2015 改成 @babel/preset-env,否则会出错(.babelrc是babel-loader的配置文件)
// 3. src下新建 plugins -> element.js 内容如下:
import Vue from 'vue'
import {Button} from 'element-ui'
Vue.use(Button)
// 4. 在main.js中引入
import './plugins/element.js'
// 此时按钮出来了,但是效果没出来,是因为babelrc没生效,解决方法如下
LOADER -> babel-loader ( 让babelrc生效,按步骤走即可 )
// 1. 先安装 npm install -D babel-loader @babel/core @babel/preset-env
// 2. 再将文档中的内容加载到 rules 中,
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          // 这里可省略,因为 babelrc 中有配置项,
          // options: {
          //   presets: ['@babel/preset-env']
          // }
        }
      }
    ]   
  }
// 注意:babel-loader 还是要加上,它能帮我们编译最新版的js
LOADER -> less-loader (使用 less 语法)
  1. 需要安装两个 $ npm install less-loader less --save-dev
  2. less 的配置需要加在这里:
```js
  {
    test: /\.(css|less)$/,
    use: [
      'style-loader',
      'css-loader',
      'less-loader'
    ]
  }
```
babel小知识:vue 中不能解析 jsx 语法,需要用到 babel-plugin 插件:
  1. 安装 babel-plugin-transform-vue-jsx 和 babel-plugin-syntax-jsx
  2. 编辑 babelrc
```js
{
  "presets": ["@babel/preset-env"],
  "plugins": ["transform-vue-jsx"]
}
```
加浏览器响应前缀(使用 postcss)

(display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;)

  1. npm i -D autoprefixer postcss-loader
  2. 在这里加上 ‘postcss-loader’
use: [
  'style-loader',
  'css-loader',
  // 'postcss-loader', 等于以下写法
  {
    loader:'postcss-loader',
  },
]
  1. 在根目录下新建 postcss.config.js ,内容如下
```js
module.exports = {
    plugins: [
      require('autoprefixer')({
          overrideBrowserslist:[
              'Android 4.1',
              'iOS 7.1',
              'Chrome > 31',
              'ff > 31',
              'ie >= 8'
            //   'last 2 versions'
          ],
        // 网格布局
          grid:true
      })
    ]
  }
```
兼容小知识:ie11 10 9 ,8 以下不能,因为8以下不兼容js语法,也就是@babel/preset-env得换
  1. 安装 npm i -D babel-polyfill
  2. 在 webpack.config.js 的 entry 里写 ‘babel-polyfill’
entry: ['babel-polyfill','./src/main.js'],

注:在main.js中引入import ‘babel-polyfill’,解决路由兼容11 10 9 问题(实际上没出错!)

环境拆分小知识:指南 -> 生产环境 -> 配置(按步骤写即可)
  1. 安装 npm install --save-dev webpack-merge
  2. 要目录下新建 webpack.dev.js 和 webpack.prod.js
  3. webpack.config.js(webpack.common.js) 里的 mode devtool devServer 都粘到 webpack.dev.js 里
  4. webpack.dev.js 和 webpack.prod.js的内容按文档粘上去,文档的内容改成自己配置里的内容
  5. 再改 package.json
"scripts": {
  // 生产环境 
  "build": "webpack --config webpack.prod.js",
  // 开发环境 
  "serve": "webpack-dev-server --open --config webpack.dev.js"
}
// 运行生产模式的时候会自动创建到设置的 dist 里,其中有个about.vue懒加载。
component:()=>import('@/views/About')
LOADER -> url-loader(图片loader,用 url-loader 代替 fileloader,按步骤走即可)
  1. 安装 npm install url-loader --save-dev
  2. 粘代码
rules: [
  {
    test: /\.(png|jpg|gif)$/i,
    use: [
      {
        loader: 'url-loader',
        // url-loader 内自带 file-loader 功能 所以下载 url-loader 的同时也需要下载 file-loader
        // url-loader 可以配置 limit,当图片文件小于 8194b 的时候使用base64 编码编译图片
        // 当图片是 base64 编码的时候,就会减少浏览器的请求,从而让页面变得更快
        options: {
          limit: 8192,
          // 图片直接引用比如 <img src="./assets/logo.png" />,会自动解析成模块,也就是不能使用,所以写下面这句代码
          esModule:false// 设置图片的存储路径以及生成后的图片名称 [name]:原始的图片名 [ext]:图片后缀名 [hash:8]:8位,用于处理浏览器缓存   结果:img/logo.26bd867d.png
          // 文件大小小于limit就会转base64码,大于的话就转成下面这种
          name:'img/[name].[hash:8].[ext]',
          // 以后项目的图片可能会托管到某个 cdn 服务器上
          // ./img/logo.png    https://www.dijia.com/xxx/img/logo.png
          // publicPath: 'https://www.dijia.com/xxx/'
        },
      },
    ],
  },
],
解析 .html 文件中的 image:
  1. 先安装 npm i html-withimg-loader -D
  2. 在配置文件里写上:
{
  test:/\.html$/,
  use: [
    'html-withimg-loader'
  ]
}
定义环境变量:DefinePlugin 允许创建一个在编译时可以配置的全局常量,这可能会对开发模式和生产模式的构建允许不同的行为非常有用。
 // 判断目前是处于什么 webpack环境下,需要 webpack 提供环境变量给我们的项目
  let url=''
  if(ENV === 'development'){
      // 开发时的后台接口基地址
      url = 'http://localhost:8080'
  }else{
      // 项目生产的时候
      url = 'https://dijiaxueshe.com/xx/ee'
  }
  console.log(url);

webpack.dev.js中(webpack.prod.js中也这样)

 plugins: [
    // webpack自带 DefinePlugin 插件可以提供环境变量
    new webpack.DefinePlugin({
      // ENV: 'development',错误,ENV是常量,认为''里是变量
      // ENV: "'development'"  这种写法也对,多数用下面那种
      ENV: JSON.stringify('development')
    })
]
// 运行用 npm run serve
创建 import 或 require 的别名:配置 -> 解析 -> resolve.alias
// webpack.config.js 中
module.exports = {
  //...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src/templates/')
    }
  }
};

// router下的index.js中,用@代替..(src)
import Home from '@/views/Home.vue'
extensions 解析的时候可以忽略扩展名: 配置 -> 解析 -> resolve.extensions
// webpack.config.js 中
resolve: {
  extensions: ['.js', '.json', '.vue']
},

// router下的index.js中,省略.vue
import Home from '@/views/Home'
modules webpack 解析模块时应该搜索的目录:配置 -> 解析 -> resolve.modules
resolve: {
  // 先在src里查找,找不到再去node_modules中查找
  modules: [path.resolve(__dirname, 'src'), 'node_modules']
},
分离 css 插件:PLUGIN -> CssMinimizerWebpackPlugin
// webpack.config.js中注释掉引入的css less
// webpack.dev.js中加上这句话
rules: [
  {
    test: /\.(css|less)$/,
    use: [
      'style-loader',
      'css-loader',
      // 'postcss-loader', 等于以下写法
      {
        loader:'postcss-loader',
      },
      'less-loader'
    ]
  },
]

// webpack.prod.js 中
// 1. 先安装  npm install --save-dev mini-css-extract-plugin
// 2. 再粘rules,加 MiniCssExtractPlugin插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

rules:[
  {
      test: /\.(css|less)$/,
      use: [
      // 可以注释掉,因为此插件自动引入 css
      // 'style-loader',
      {
          loader: MiniCssExtractPlugin.loader,
      },
      'css-loader',
      'postcss-loader', 
      'less-loader'
      ]
  },
] 

plugins: [
  new MiniCssExtractPlugin({
      filename:'css/[name].css',
      chunkFilename:"css/[id].css"
  })
压缩 css 优化:配置 ->优化 -> optimization.minimizer
// webpack.prod.js 中
// 1. 安装:npm i optimize-css-assets-webpack-plugin
// 2.
const OptimizaCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = merge(baseConfig, {
    // webpack 提供一些代码的优化功能,需要使用optimization配置,比如配置css和js的优化
    optimization: {
        minimizer:[
          // 压缩 css
            new OptimizaCSSAssetsPlugin()
        ]
    },
}
压缩 js 优化:配置 -> optimization.minimizer
  1. 先安装 npm i -D terser-webpack-plugin@2
  2. 再粘文档
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        cache: true, // 缓存生成的 webpack 模块和 chunk
        parallel: true, // 多进程并发运行
        // 配置 -> minimizer ->  TerserPlugin -> terserOptions -> 配置 -> Minify options -> Compress options -> pure_funcs  ........
        terserOptions:{   
          compress: {
            // 压缩js的时候忽略console,控制台没有输出
            pure_funcs: ['console.log']
          }
        }
      }),
    ],
  }
};
清理 dist 文件夹(打包时修改之后之前的也会存在,所以清理):指南 -> 管理输出 -> 清理/dist文件夹(按步骤即可)
  1. npm install --save-dev clean-webpack-plugin
  2. const { CleanWebpackPlugin } = require(‘clean-webpack-plugin’);
  3. new CleanWebpackPlugin(),
打包出口 js 到文件夹内
  1. 把 webpack.config.js 内的 output 删掉
  2. 在 webpack.dev.js 内粘上:
const path = require('path');
output: {
  filename: 'bundle.js',
  path: path.resolve(__dirname, 'dist')
},
  1. 在 webpack.prod.js 内粘上并修改:
const path = require('path');
output: {
  // 加的js是文件夹的名字,hash 是处理缓存
  filename: 'js/bundle.[hash:8].js',
  path: path.resolve(__dirname, 'dist')
},
打包字体 fonts 到文件夹内:LOADER -> file-loader -> outputPath
  1. 把 webpack.config.js 内的 .woff 等关于字体的删掉
  2. 原样的复制到 webpack.dev.js 中 和 webpack.prod.js 中(prod内需要修改)
{
  test: /\.(woff|woff2|eot|ttf|otf)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        // 按照上面的文档复制过来,配置写的是文件夹的名字
        outputPath: 'fonts',
      }
    }
  ]
},
解决背景图地址出错的问题(打开dist下的index.html)
{
  // webpack.prod.js中
  // 直接在MiniCssExtractPlugin这里加个配置,设置下公共路径publicPath
  loader: MiniCssExtractPlugin.loader,
  options: {
    // 背景图的地址一打包后就会出错,所以改一下公共路径
    publicPath: '../'
  }
}

注:直接打开 index.html ,路由可能会出现问题,此时可以在dist文件夹下,输出 serve . 在5000里打开就不会出错

// 研究promise的小例子
const newNumber = await new Promise(resolve =>{
    setTimeout(()=>{
        resolve(10000)
    },1000)
})
this.number= newNumber
webpack中解决跨域请求做代理:配置 -> 开发中 -> devServer.proxy
// 在开发环境中
devServer: {
  proxy: {
    '/api': {
      target: 'https://www.vue-js.com/api/v1',
      changeOrigin: true, // 需要虚拟的主机地址
      pathRewrite: {"^/api": "" } // 带着api的请求要发到target里,然后必须重写api
    }
  }
},
// 使用
const info = await axios.get('api/user/sunny-zwy')
vue-cli 部署(上传到 github)

把代码打包到dist文件,复制到webpack-test仓库,上传到github 访问网址https://sunny-zwy.github.io/webpack-test/#/
注:此时网上路径会出错,需要加一下配置 vue.config.js :vue-cli -> github pages

module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/webpack-test/'
    : '/'
}

mock 学习 (vue)

  1. 创建项目,项目下安装
npm install axios --save
npm install mockjs --save-dev
npm install json5 --save-dev // 解决json文件不能添加注释的问题(可有可无)
  1. 在根目录下新建 mock 文件夹 -> testMock.js(项目中用的不是它)
// 先导入mock, 然后在模拟你的数据(详情看官方文档)
const Mock = require('mockjs')
// 第一种返回一个简单的字符串
let id = Mock.mock('@id')
// 第二种返回的是对象
var data = Mock.mock({
    id: "@id()",
    username: "@cname()",
    date: "@date()",
    avatar: "@image('200x200','red','#fff','avatar')",
    description: "@paragraph()",
    ip: "@ip()",
    email: "email()"
})
// 可以用 `node 文件名` 运行
  1. 在 mock 下新建 userInfo.json5 文件(可以把生成的对象放进去)
{
    id: "@id()",
    username: "@cname()",
    date: "@date()",
    avatar: "@image('200x200','red','#fff','avatar')",
    description: "@paragraph()",
    ip: "@ip()",
    email: "email()"
}
  1. 在新建 testJSON5.js,在这里读取 json5 文件
const fs = require('fs')
const path = require('path')
const JSON5 = require('json5')

var json = fs.readFileSync(path.join(__dirname,'./userInfo.json5'),'utf-8')
// 上面的json是字符格式的,不是对象,需要用下面的方法转换
var obj = JSON5.parse(json)
console.log(obj); 
  1. 在项目根目录下,新建 vue.config.js
module.exports = {
    devServer:{
        before:require("./mock/index.js")
    }
}
  1. 在 mock 下新建 index.js
const fs = require('fs');
const path = require('path');
const Mock = require('mockjs'); //mockjs 导入依赖模块
const JSON5 = require('json5');
// 读取json文件
function getJsonFile(filePath) {
    // 读取指定json文件
    var json = fs.readFileSync(path.resolve(__dirname,filePath),'utf-8');
    // 解析并返回
    return JSON5.parse(json)
}

// 返回一个函数
module.exports = function(app){
    // 监听http请求,if判断是什么意思请看 8.
    if(process.env.MOCK == 'true'){
        app.get('/user/userinfo',function(rep,res){
            // 每次响应请求时读;
            // 取mock data的json文件
            // getJsonFile方法定义了如何读取json文件并解析成数据对象
            var json = getJsonFile('./userInfo.json5')
            // 将json传入Mock.mock方法中,生成的数据返回给浏览器
            res.json(Mock.mock(json))
        })
    }
}
  1. 此时mock就写好了,在vue页面中可以调用
import axios from 'axios'

axios.get('/user/userinfo').then(res => {
    console.log(res);
}).catch(err => {
    console.error(err);
})
  1. 根目录下新建一个 .env.development 的文件
// 如果后台写好了,那么把 MOCK 值变为 false
MOCK = true

mock 学习(jquery中)

  1. 新建 mock 文件夹,里面有 index.js
Mock.mock('/user/userinfo','get',{
    id: "@id()",
    username:"@cname()",
    date:"@date()",
    ip:"@ip()",
    email:"@email()"
})
  1. 根目录下的 index.html , 线上引用mock、jquery
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdn.bootcss.com/Mock.js/1.0.0/mock-min.js"></script>

<!-- 如果后台写好了可以注释掉下面的引用 -->
<script src="./mock/index.js"></script>
<script>
  // 第一种可以写成的请求方式
  $.ajax({
      url: '/user/userinfo', // 默认是 get
      dataType:'json', // json类型转成对象
      success:(data)=>{
          console.log(data);
      }
  })
  // 第二种可以写成的请求方式
  $.get("/user/userinfo", function (res) {
      console.log(JSON.parse(res))
  });
</script>

小案例

滚动条在底部

scrollToBottom() {
    this.$nextTick(() => {
      var container = this.$el.querySelector('.dom')
      container.scrollTop = container.scrollHeight
    })
}

回车发送

document.onkeydown = function (e) {
    let key = window.event.keyCode
    if (key == 13 && this.sendContent!='') {
      this.sendInfo() //发送事件
      e.preventDefault();
    }
}

axios的使用

方便的使用方法:在 main.js 里导入axios,window.axios = axios, 页面中直接使用即可。

websoket

一百度就有,记住流程就可以。另外public -> config.js一般都是引在html中的,

window.data= {
  a: 'ws:192.168.1.121:8080',//websoket的地址,页面直接使用即可。
};
data(){
  return(){
    path:'后台地址',
    websoket:''
  }
},
mounted () {
    // 初始化
    this.init()
},
methods: {
    init: function () {
        if(typeof(WebSocket) === "undefined"){
            alert("您的浏览器不支持socket")
        }else{
            // 实例化socket
            this.socket = new WebSocket(this.path)
            // 监听socket连接
            this.socket.onopen = this.open
            // 监听socket错误信息
            this.socket.onerror = this.error
            // 监听socket消息
            this.socket.onmessage = this.getMessage
        }
    },
    open: function () {
        console.log("socket连接成功")
    },
    error: function () {
        console.log("连接错误")
    },
    getMessage: function (msg) {
        //后台推送过来的数据在这里接收
        console.log(msg.data)
    },
    send: function () {
        this.socket.send(params)
    },
    close: function () {
        console.log("socket已经关闭")
    }
},
destroyed () {
    // 销毁监听
    this.socket.onclose = this.close
}

下载(导出)文件

利用后台地址导出文件(文件流)

httpUtils 文件夹

/**
 * @param URL 下载地址
 * @param mode 下载方式 get post
 * @param name 下载文件名
 * @param param 参数
 * @param fileType 下载文件格式
 */
function downloadUrlMode(url, mode, name, param, fileType) {
  const promise = new Promise((resolve, reject) => {
    axios({
      url: url,
      method: mode,
      data: param,
      headers: {
        Authentication: sessionStorage.getItem('token'),
        Accept: 'application/json'
      },
      responseType: 'arraybuffer'
    }).then(response => {
      const blob = new Blob([response.data], {type: 'application/' + fileType})
      resolve(response.data);
      const fileName = name + "." + fileType
      let link = document.createElement('a')
      link.href = URL.createObjectURL(blob)
      link.download = fileName
      document.body.appendChild(link)
      link.click()
      window.setTimeout(function () {
        URL.revokeObjectURL(blob)
        document.body.removeChild(link)
      }, 0)
    })
  });
  return promise;
}

export default {
  downloadUrlMode: downloadUrlMode,
};

sysUserApi文件夹

import httpUtils from "@/core/utils/http.utils";
// service path
const urls = {
    'exportExcel': `/api/upms/sysUser/exportUserExcel`
};
// service methods
const sysUserApi = {
    exportExcel(params){
        return httpUtils.downloadUrlMode(`${urls.exportExcel}?sysOrgId=${params.sysOrgId}`, 'get','部门列表',params,'xlsx');
    }
};

export default sysUserApi;

vue 文件中

import sysUserApi from "./api/sysUser.service";

sysUserApi.exportExcel().then((res) => {
  console.log(res.data)
});
没有后台,前端本地项目中的文件(vue-cli 3+)

文件必须放在public文件夹下,放别处都不生效,新名字的扩展名可有可无

<a :href="`${path}article.xlsm`" download="新名字.xlsm">下载本地模板</a>
data() {
  return {
    path: process.env.BASE_URL // 这里指得是 public(/)
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值