网上搜索jquery插件,点击这里
## swiper插件
网上搜索swiper插件,点击这里
网上搜索wow.js下载,进入官网,点击这里
网上搜索moment.js,进入官网点击这里
// 这种包是基础的
//
// 这种包是基础并且多语言的,两者引其一
<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日'))
console.log(num1)
var num1=10
// (undefined),声明提升
```
var a = undefined
a.grade
// 不能设置undefined的grade属性,例子不好,马马虎虎看吧
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) 不是一个数字的数字 数学计算出错
var num = 15.87
var str = String(num)
var str1 = num.toString()
console.log(typeof str, typeof str1);
// string string
// 数据类型的检测:typeof
var num = ''
var bool = Boolean(num)
console.log(bool)
// false
// false:0,'空',undefined,null,NaN
// true:'空格',数字
// 赋为任意值就为真,赋为 0,'空',undefined,null,NaN,就为假
if(num = 10){
console.log(1);
}
var a = '6'
var b = '7a'
console.log(a % 5, b * 5);
// 1 , NaN
var num = 10
var str = '10'
console.log(num == str, num === str)
console.log(num != str, num !== str)
// true, false
// false, true
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 才跳出循环,不然继续执行到最后
// 输出 1-10
var i = 0
while(i < 10){
i++
console.log(i);
}
// 输出 1-10
var i = 0
do {
i++;
console.log(i);
}
while (i<10)
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,就默认是全局的变量
function fun(){
console.log('毕业见!!')
}
fun()
var fun = function () {
console.log('毕业见!!')
}
fun()
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('我是最新品')
}
// 此例子对应的不是上面的,而是我认为的难点!
var obj = {
name:'小张'
}
var obj1 = obj // 拷贝了一个地址
obj1.name = '小王'
console.log(obj.name); //小王
console.log(obj1 === obj); // true
var arr = [1, 2, 3]
var length = arr.push(5)
console.log(arr,length)
// [1,2,3,5] 4
var num = arr.pop()
console.log(arr, num)
// [1,2,3] 5
var length1 = arr.unshift(0, 1)
console.log(arr, length1)
// [0,1,1,2,3] 5
var num1 = arr.shift()
console.log(arr, num1)
//[1,1,2,3] 0
//shift括号里填任何值都无用
var arr1 = [8, 9]
var arr2 = arr.concat(arr1)
console.log(arr, arr2)
//[1,1,2,3] [1,1,2,3,8,9]
//arr.splice(a,b,c) a添加或删除的开始位置 b 删除的个数 c 添加的元素
var newArr = arr.splice(0, 2, 9)
console.log(arr, newArr)
// [9,2,3] [1,1]
//arr.slice(a,b) a,b 代表的都是位置 原则包括 a 不包括 b
var newArr1 = arr.slice(0, 2)
console.log(arr, newArr1)
//[9,2,3] [9,2]
var ind = arr.indexOf(10)
var ind1 = arr.indexOf(3)
console.log(arr, ind, ind1)
//[9,2,3] -1 2
var bool = arr.includes(8)
var bool1=arr.includes(9)
console.log(arr, bool,bool1)
//[9,2,3] false true
var newArr2 = arr.reverse()
console.log(arr, newArr2)
//[3,2,9] [3,2,9]
//arr.join('') 可加任何符号,都用来拼接
var str = arr.join()
// 以逗号拼接, 3,2,9
var str=arr.join('')
//直接拼起来 329
var str=arr.join('--')
// 3--2--9
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 是降序
var arr1 = [1, 2, 3, 4, 5]
var arr = arr1.every(function (ele, index, array) {
return ele > 0
})
console.log(arr)
// true
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
var arr1=[1,2,5]
var arr2 = arr1.map(function (ele, index, array) {
return ele * 2
})
console.log(arr2)
// [2,4,10]
// 函数内的返回值会被当作新的数组内的值
var arr1 = [1, 2, 5]
arr1.forEach(function (item, index, array) {
console.log(item)
})
// 1 2 5
// 大多情况用来遍历对象数组
var newArr = arr1.filter(function (item) {
return item > 1
})
console.log(newArr)
// [2,5] 返回新数组
var arrObj = [
{
name: '小黑',
age: 18
},
{
name: '小黑1',
age: 21
}
]
var bool = arrObj.some(function (ele) { return ele.age > 20 })
console.log(bool)
var str = 'joijhadsa111111'
console.log(str.charAt(5))
// a
var str = 'bajoijhads111111'
console.log(str.charCodeAt(1))
// 97
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 变成全局替换了
// 今天天气不错,最高气温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"]
var str = 'ajoijhads11111v1'
console.log(str.slice(0, 3))
// ajo
var str2 = '1-2-345-6'
console.log(str2.split('-'))
// ["1", "2", "345", "6"]
var num=0.1+0.2
console.log(num)
// 浮点型运算 运算的误差 0.000000000004 左右 四舍五入可以去掉
var num1 = num.toFixed(2)
console.log(num1,num)
// 0.30 0.30000000000000004
var str = ' sadef '
console.log(str, str.trim())
// ' sadef ','sadef'
字符串新增方法:startsWith,endsWith(参数字符串是否在原字符串的头尾部),repeat,trimStart,trimEnd(消除头部空格)
Math.random() 0-1 的随机数,包括 0 ,不包括 1
Math.floor(3.94) 3 下舍
Math.ceil(3.94) 4 上进
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 = $('+ goodsArr[i].goodsPic + '" />')
// 模板字符串
var goodsDiv = $(`${goodsArr[i].goodsPic}" />`)
}
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 距现在时间的毫秒数 用于创建一个永远不会重复的数
var run = setInterval('update()', 1000);
var num = 0
function update() {
num++
if (num == 5) {
//clearinteval只终止定时器不终止函数
clearInterval(run)
// return 想终止函数可以用return
}
console.log(num);
}
//setTimeout 只执行一次,而setInterval执行多次
function hello() {
console.log("setTimeout");
}
//使用方法名字执行方法
var t1 = setTimeout(hello, 1000);
var t2 = window.setTimeout("hello()", 3000);//使用字符串执行方法
window.clearTimeout(t1);//清除定时器
//两个一起用一般是用来倒计时的,最后显示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)
}
}
var arr = [1, 2, 3]
function fun1(ele) {
return ele >= 2
}
var arr1 = arr.filter(fun1)
console.log(arr1)
// [2,3]
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$/
// [email protected] 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
var re = /[abc]/
//这个正则的意思是一位 a 或 b 或 c
var re1 = /[^abc]/
//这个正则的意思是一位非(a或b或c)的字符
var re2 = /[^0-9]/
//这个正则的意思是一位非数字的字符
console.log(window.document)
// 输出的是整个页面的代码
var num = 10
console.log(window.num)
function fun() {
console.log(11)
}
window.fun()
// 10 11
```js
document.getElementById('id')
// 通过 id 名获取 **只能获取一个**
document.getElementsByTagName('标签名')
// 通过标签名获取 获取的一个**类数组** 里面存的是真正的节点
document.getElementsByClassName('class名')
// 通过class名获取 获取的一个**类数组** 里面存的是真正的节点
document.getElementsByName('name名')
// 通过name名获取 获取的一个**类数组** 里面存的是真正的节点
document.querySelector('.box')
// 通过css选择器获取 **只能获取第一个**
document.querySelectorAll('css 选择器')
// 通过css选择器获取 获取的一个**类数组** 里面存的是真正的节点
```
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 的类名字
document.querySelector('div').className
// 修改 div 的类名字
document.querySelector('div').className = '另一个类名'
// 添加 div 的类名字
document.querySelector('div').className = document.querySelector('div').className + ' 另一个名字'
document.querySelectorAll('.btn')[0].onclick = function () {
document.querySelector('.box').style.width = '500px'
}
document.createElement('标签名) $('')
append 或 appendChild a.appendChild(b) a是父级 b是子级
insertBefore a.insertBefore(b,c) a是父级 b是要添加的 c是a中存在的,在c前面加b
a.removeChild(b) a是父级 b是子级 b.remove()
属性: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]
W3c 里有,(点击这里)[https://www.w3school.com.cn/jsref/dom_obj_window.asp]
document.querySelector('.back').onclick = function () {
// 后退两个页面
history.go(-2)
// 后退一个页面 = history.go(-1)
history.back()
// 前进一个页面 = history.go(1), go(2)也行
history.forward()
}
document.querySelector('button').onclick = function () {
// 实现页面跳转,覆盖掉当前页面
location.href = 'https://www.baidu.com'
// 直接 log
console.log(location.href)
// 实现页面跳转,打开新的一页
window.open('https://www.baidu.com')
}
第一轮: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 :调用该函数的对象
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()
// 当 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
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
function fun() {
"use strict";
num = 100
}
fun()
console.log(num)
// 不使用严格模式,num为全局,可以输出100,使用严格模式就会报错
var a = 5
function fun() {
"use strict";
console.log(this.a)
}
fun()
// 不使用严格模式,this指向window,使用严格模式,输出 undefined
"use strict";
var obj = {
name: 'Lily',
age: 18
}
delete obj.age
console.log(obj)
// delete可以用来删除变量,结果为 {name:'Lily'}
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)
// '小红' '小红'
var newArticle = { ...article }
// 效果同上
var newArticle = {}
for (var key in article) {
newArticle[key] = article[key]
}
// 效果同上
var newArticle = article
// 效果是里外都能修改,哪层都没拷贝好!
// JSON.parse 将 json 字符串转化成对象
// JSON.stringify(obj) 将对象变成 json 字符串
var newArticle = JSON.parse(JSON.stringify(article))
// 修改新数组里层外层,原数组不变
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' })
// 这种颜色是粉色,背景色是灰色
小知识:对象的合并
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
console.log(Object.keys(goods))
// [id,name,price]
需要使用命令行,安装 git bash 即可。
查看当前目录下的所有内容
命令参数
-a
查看当前目录下的所有内容包括隐藏文件跳转目录
特殊的目录
创建文件夹
创建文件,需要加后缀名
删除文件或文件夹
参数
打印当前位置
复制或重命名
参数
剪切
查看文件里面的内容
不需要选择安装目录,一直下一步,直到出现很多复选框其中有两个是 git bash here
和 git gui here
将后面的勾选掉(不勾选),继续一直下一步即可。安装完毕之后在任意空白处点击鼠标右键就会出现 git bash here
命令。
git clone 仓库地址
git add .
git commit -m'留言'
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
执行完毕之后就成功了
git init
命令 git remote add origin https://github.com/Sunny-zz/first_demo.git
这行命令作用是给本地的仓库添加一个远端地址 名称叫 origin 地址是 后面的地址
git push -u origin master
将版本上传到已经添加的远端 origin
将自己的电脑的当前系统和 github 关联。在上传或下载的时候使用 ssh 方式,省略输入用户民和密码。关联步骤如下
cd ~
命令ssh-keygen
命令,生成电脑上的公钥和私钥,直接一直回车直到出现密码图即可。~/.ssh/
文件夹下,使用 cat 命令打印出公钥内容并复制 cat .ssh/id_rsa.pub
cd .ssh
在执行 ls -a
查看自己的秘钥名称cat 你的公钥
去复制。同事 a 对 first_demo 仓库进行了修改,在 index.html 内添加了 一个轮播图,上传提交成功
同事 b 被要求添加一个 about 页面在项目内,做完之后上传执行 git 上传三部曲。执行 git push
失败提示远端存在本地不存在的版本,可以使用 git pull
命令。
执行 git pull
将远端的版本拉取到本地,但是本地已经存在了一个为提交的版本,
当这两个版本不冲突的时候(不时同一个文件,或者同一个文件的不同位置),
当这两个版本冲突的时候(修改的是同一个文件的同一个位置)
一个仓库内可以有多个分支,默认只有一个分支 master ,通常称为主分支(用来放合并后的代码)。也可以创建无数个其他分支。一般工作流程是先在主分支将项目的主体框架搭建完毕,然后创建多个分支,每个分支代表不同的功能,不同的程序猿分别在不同分支内进行开发,开发完毕将代码合并到主分支。
git branch a
和 git branch b
命令。分支创建的时候里面的内容和主分支是一模一样的。git push
就会成功,但是由于网上并没有新分支,所以需要使用 git push --set-upstream origin yourbranch
上传。git merge yourbranch
命令合并分支。合并之后上传。拿我们创建好的 branch_demo 仓库来说,master 分支内已经存放好了其他分支做好的项目(项目成品,包括 index…),github 仓库有一个特殊的分支叫 gh-pages ,该分支下的内容会被自动托管到 github 免费服务器(也就是说只要该分支下有 index.html 就可以直接使用网址访问)
只需要直接创建 gh-pages 分支,并上传到网上即可。
gitignore 文件是一个隐藏文件,该文件的作用是当你将一些文件或文件夹的名称写在 .gitignore 内的话,该仓库执行上传操作的时候,会忽略 .gitignore 内添加的文件或文件夹
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
创建新的分支并切换过去let const 和 var 的不同
const 是声明常量(值不可修改,例如 π)的,常量的名称都是全大写的(单对于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
// 1.
const add = (num1, num2) => {
return num1 + num2
}
// 2.
const add = (num1, num2) => num1 + num2
// 调用
const res = add(10, 20)
console.log(res)
// 30
箭头函数和普通函数的区别
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()
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)
第七种数据类型,生成独一无二的数据
类似于数组,但是不能存重复的值
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 结构的实例有四个遍历方法,可以用于遍历成员。
还有一个额外的 WeakSet 数据结构,内部成员只能是对象类型
写法
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.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。
安装
直接百度 node 进入中文网直接下载安装包,直接进行安装不需要做任何的选择,一直下一步即可。
任意位置打开命令行工具,输入 node -v
和 npm -v
查看 node 和 npm 的版本号,如果有就安装好了,如果命令出错尝试重启电脑再次执行。
npm 是什么
安装好 node 之后 npm 就附带安装好了。他是 node package manager,node 包管理工具。
node 模块(后台,前端)
前端模块包括前端所有的 js 相关的包。
node 模块使用
npm init -y
npm install jquery
require(包名)
npm 的使用
npm 就是下载 node 包的工具。
下载方式有三种
npm i 包名 --save
,这种方式一般下载的是项目的必须依赖,记录到 package.json 内的 dependencies 字段内。 npm install [email protected] --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 下载包的好处
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 项目内使用 node 模块导入各种依赖,webpack 可以实现将模块的导入导出编译成浏览器认识的语法,也可以将所有的导入模块操作打包。
如何使用 参考链接
npm install webpack webpack-cli --save-dev
npx webpack
,会将 index.js 打包编译到项目下的 dist 文件夹下的 main.jswebpack.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
,复制基础的配置到该文件内。"build": "npx webpack --config webpack.confi.js"
)es6导入两种方式
导出两种种方式
// 模块路径 第三方和核心模块直接写包名
import x from "./index.js"
export default 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;
Asynchronous JavaScript and XML(异步的 JavaScript 和 XML).
在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
现在已经进阶了,进阶成 Asynchronous JavaScript and JSON。
// 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));
}
};
$.get("http://jsonplaceholder.typicode.com/posts", function (res) {
console.log(res)
});
$.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 请求插件,它里面的异步解决方案使用的是 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
npm i -g json-server
可能有点慢,安装一次就够了{
"users": [
{
"id": 121,
"username": "小王"
},
{
"id": 21,
"username": "小二"
}
]
}
json-server --watch xxx.json -p 3000
,命令执行完毕之后,数据库就启动了,不要关闭该服务。安装上述 json 文件,启动的服务可以使用 ‘http://localhost:3008/users’ 接口就能访问用户列表了。npm i -g serve
,安装一次就够了serve .
http://localhost:5000
服务器上,默认打开 index.html项目的开发阶段,前端页面跑在本地(局域网)服务器上,后台数据库服务器只能公司内部访问。
想要使用 vue 开发项目,需要安装 vue 的开发环境。官方网站提供了安装环境的脚手架工具 vue-cli。使用命令 npm install -g @vue/cli
全局安装脚手架工具。工具安装好之后就可以搭建 vue 的开发环境了。有两种方式搭建
vue create 项目名
。vue ui
在浏览器中调出图形化界面,创建 vue 项目。
vue 项目创建的就是单页面应用。整个项目就在一个页面内。参考文档单页面
整个 vue 项目就是由各种各样的组件组合而成的。组件可以理解成我们原来排版的某一个结构部分。app.vue 组件是项目的最外层结构。在 vue 项目中,最简单的组件写法就是以 vue 为后缀名的文件,组件名称一般首字母大写,多个字母使用大驼峰方式命名。
如何划分组件,其实就是和之前画盒子一样。
vue 后缀的组件构成
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 结构内
组件的样式基本上都是全局的,因为只有一个页面,所有的组件都会渲染到同一个页面。
<script>
// 第一步,先在父组件内导入子组件
import Header from "./components/Header";
// 在组件内js部分必须默认导出一个对象,而且该对象下一般必须设置一个属性 name 属性值可以和组件名一致,不能和 html 标签命名冲突
export default {
name: "App",
// 第二步 组件注册
components: {
// Header: Header,
// 对应的是在wrap里引用的那个:上面导入的变量
Header,
},
};
</script>
<style scoped>
// scoped 的作用就是给该组件的所有标签加上一个随机属性(和所有组件都不同),所有选择器都添加上了该属性的属性选择器
// 那么加了 scoped 属性的样式就变成了私有样式了
</style>
<div class="wrap">
<header />
div>
当一个组件需要在很多个组件内使用,而且多多少少显示的内容不一样,其实是需要根据组件所在位置进行轻微的修改。此时就可以借助 vue 内的 props 知识点处理。
父组件嵌套子组件的时候希望子组件要根据我的想法修改一些内容。
props 的使用
<Button value='warning' text="Download Now" color="#00f" :isActive="true" />
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;
},
},
}
};
在标签的尖括号之间使用,直接用双花括号嵌套 props 名称即可
<button class="btn">{{text}}button>
在标签的属性内使用,需要使用 vue 指令 v-bind:
也可以直接省略使用 :
<button :style="`background-color: ${color}`" class="btn">按钮button>
我们在介绍 prop 的时候就已经使用了 vue 的模板语法。在 template 中嵌入 js。
分为两大类
v-bind
也可以直接省略使用 :
使用方法
export default {
name: 'App',
data: function () {
return {
bgColor: 'red',
}
},
...
<div v-bind:style="`background-color:${bgColor}`" class="box">
<button>单机变蓝button>
div>
this.名
访问this.名
重新赋值,这种表较常用直接使用 v-on 指令绑定事件,也可以简写成 @ ,vue里没有hover事件,可以写成mouseenter事件
<button v-on:click="change()">单机变蓝button>
<button @click="change()">单机变蓝button>
<button @click="function(){bgColor='blue'}">单机变蓝button>
<button v-on:click="()=>{bgColor='blue'}">单机变蓝button>
<button v-on:click="bgColor='blue'">单机变蓝button>
<span>成绩:{{grade}}</span>
<br />
// 由于函数没有返回值所以update(190)的返回值是undefined,而vue指令的功能可以这样写,还可以写成: -->
// -->
// 以上 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是从事件函数那来的
},
},
爷孙组件
父子组件
<Span :slide1="slide" text="asdsdfsf" />
自定义事件, 向子组件传递函数,一般是当父组件的 data 想要让子组件修改时使用
给子组件设置 ref
父组件中
<Content ref="ceShi" />
<button @click="handle">测试refbutton>
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 获取父组件实例
v-for="item in $parent.shuJi"
mounted() {
console.log(this.$parent);
},
插槽(slot)
<Button v-model="isActive">哈哈哈Button>
<button>
<slot>slot>
button>
<template #list>
<div class="list">列表div>
template>
<div>
<slot name="list">slot>
div>
<slot v-bind:obj1="obj" />
<ScopedSlotDemo>
<template v-slot:default="slotProps1">
<span>{{ slotProps1.obj1.age }}span>
template>
ScopedSlotDemo>
<ScopedSlotDemo v-slot:default="slotProps">
<span>{{ slotProps.obj1.age }}span>
ScopedSlotDemo>
<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)">修改父组件的bbutton>
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>
<button @click="x = 20">修改provide里的x的值button>
// 和data同级
export default {
provide: {
x: 100,
},
}
子组件内
<div>使用父组件 provide 的数据:{{ x }}div>
export default {
name: "Son1",
inject: ["x"],
};
为了让父组件能拿到自己的值,我们一般写成函数,如下:
父组件内
<div>父组件内的 number:{{ number }}div>
<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, attrs,listeners
app内:
<Parent2 :a="10" obj="小花" @x="() => console.log(1)" />
Parent2内:
<Son2 v-bind="$attrs" v-on="$listeners" />
兄弟组件
<Button @change1="change">Button>
<Button @change1="change(index)">Button>
子组件内
js 内
methods: {
handleClick() {
// 接收父组件传递过来的函数并调用
this.$emit("change1");
// 这是传参调用
// this.$emit("change1",4);
}
}
当你要实现子组件同步父组件的 data 时,一般采取父组件内定义事件传递给子组件执 行的方案,此方案可以使用 .sync 修饰符简化
自定义事件的基础写法,组件名写成 update:title
父组件内的两种自写法
<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.native="change">Button>
子组件想要修改父组件的 data ?
为什么选择一个 data 不设置两个?
如果图片的 src 地址写成了 js 相关的值,那么图片不会解析在浏览器上,解决方案:
html
<li :locate="require('../'+item.locate+'.jpg')">li>
js
liArr:[{locate:"assets/p1"}]
以后我们自己做项目的话可能会用到一些图片,这些图片写成本地的话不太好,将我们的图片上传到 github 或者 腾讯云 或者其他的免费网站
报错 Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. 意思是:请避免直接修改 prop ,在父组件重新渲染的时候该属性会被重写(避免在子组件内修改父组件传过来的data)
在 template 中当事件函数内想要使用事件对象请使用 $event
<input type="text" @input="$emit('change-searchvalue',$event.target.value)" />
三目运算符的优先级低于 &&
if语句的简写方式,条件成立时,执行后面的语句,后面的语句必须和if在同一行
if(value) console.log(111),(console.log(222))
else console.log(333)
想按回车就添加,就v-on:keyup 或者可以keydown都可以,回车的编码是13,把enter 换成13也对
<input
type="text"
v-model.trim="value"
@keyup.enter="handleClick"
/>
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 即可
字体图标和自动生成代码(自定义代码片段)的视频是2020.8.31
生命周期是同步的,发送请求是异步的
bootcdn->twitter-bootstrap:字体样式,复制到index.html中即可使用字体
想要使用axios插件,需要在依赖下安装,在代码中引入
import axios from 'axios'
computed 该计算属性是在页面初始的时候就执行,所以在父组件的 computed 内无法获取子组件实例,也就是不可以使用 $refs 和 $children(因为子组件的created还渲染完,涉及到生命周期问题),computed里也不能做异步操作,不允许。
想要有间隔的触发,而不是每点击每触发 (京东,老师推荐用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
}
})
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);
// 想要显示在页面上,还需要添加上这一条对象,要不然是后台更新了,前端还没更新,不过一刷新就又有了
});
代码片段:按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"
}
当每个按钮对应每个点击事件时,没有必要写三个事件,可以写成一个 xx(属性名,属性值){this[名]=值}
<button @click="x(one,'one1')">按钮button>
<button @click="x(two,'two1')">按钮button>
methods: {
x(name, zhi) {
this[name] = zhi;
},
},
Object.defineProperties 给对象添加一个或多个属性或方法,或者修改现有的属性
const obj = { a: 10, b: 30 }
Object.defineProperties(obj, {
b: {
get() {
return 20;
}
},
});
console.log(obj.b);
运行依赖:项目必须(运行)依赖(axios vue-router element-ui …) ,项目没有这个包不可能运行 --save -S
开发依赖:(sass less stylus …) 工具类的(编译工具,插件类辅助工具) --save-dev -D
addEventListener和click的区别在于:两个click后面的会覆盖前面的,而两个addEventListener都触发,removeEventListener只能解绑addEventListener触发的事件
const fun = function(){
console.log(11)
}
document.addEventListener("click", fun);
document.removeEventListener("click", fun);
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);
模块内自带作用域,如果没有导出,别处是拿不到模块内的变量的
组件内部想要调用自己必须先声明name属性,当作标签名,外面必须加判断
<template>
<div>
<div v-if="num > 4">
<Cascader />
div>
div>
template>
<script>
export default {
name: "Cascader",
data() {
return {
num: 5,
};
},
};
script>
<style>style>
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
}
获取到当前页面锚点链接(#)
const path= window.location.hash
console.log(path)
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
}
}
$('.btn1').off('click');
// off方法,能单机但不管用
$('.btn1').attr('disabled', true)
// 不能单机,变灰色
document.querySelector('.right').onclick() = function(){
console.log('一直在执行,取消用clearInterval')
}
setInterval(function(){
document.querySelector('.right').onclick()
// jquery $('.right').trigger('click')
// $('.right').click() 也行,但是不太好
},1000)
// ul 是变量,用 tagName 可以获得子级 li(得到的标签名是大写的) 的 this,再操作 this
ul.onclick = function(event){
if(event.target.tagName==='LI'){
// 得到的是点击的这个 li
console.log(event.target)
}
}
$('div').click(function () {
console.log('父级');
})
$('span').click(function () {
console.log('子级');
// 阻止了之后单击子级只出现'子级'
event.stopPropagation()
})
<div @click.stop class="dialog">
<button @click.prevent>确定button>
<a href="https://www.baidu.com" target="_blank">跳转</a>
var user = {
name: 'lucy',
like: 'eat',
say(){
console.log('说')
}
}
for (var i in user) {
// i 代表对象的3个属性名
// user[i]代表对象的 3 个属性值
console.log(i,user[i])
}
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) // 依次输出值
}
var a = []
if (a) {
// 只有确定的那几个数是false 别的都是true,空数组也是true,log 1
console.log(1)
// 数组 a 里没有 b ,输出 undefined
console.log(a.b)
}
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 ...
}
}
function people(name) {
console.log(arguments[0]);
// 'zhang' ,arguments是一个数组,内容是传过来的参数
}
people('zhang')
console.log($('.box').css('background-color'))
// 获取css的背景色,如果box重名,永远获取第一个盒子
// 先获取行内样式,没有再获取上面的样式
$('.box').attr('class','xxx')
// 修改class的类名为xxx
// window.getComputedStyle(元素,null)
var box = document.querySelector('.box')
window.getComputedStyle(box,null).width
将一组数据循环渲染到页面上,需要使用指令 v-for
循环渲染两种情况
<li v-for="(item,index) in arrList" :key="index">{{item}}li>>
<li v-for="item in arrList" :key="唯一值">{{item}}li>
<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">1span>
<span v-else-if="ind==2">2span>
<span v-else-if="ind==3">3span>
<span v-else>4span>
style 行内样式 和 class 名在 vue 组件内的多种写法,其实就是为了更简单的去实现样式的修改。
style 绑定
<div class="box" :style="styleObj">stylediv>
data: function () {
return {
isHas: true,
styleObj: {
color: "red",
fontSize: "50px",
},
};
},
class 绑定
<div class="box">div>
<div :class="{box:true,active:true}">stylediv>
<div :class="['box',isHas?'active':'']">div>
<div :class="['box','active']">div>
<div :class="['box',{active:true}]">div>
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;
}
新方案:
<input type="text" v-model="username" />
data:function(){
username:'名字'
//此时username随便内容变化而变化,不用写事件
}
v-model 指令的修饰符
<input type="text" v-model.trim="password" />
你想对一个 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];
}
}
},
当你的计算属性需要根据异步操作来计算,但是计算属性函数内要直接返回结果,不能添加异步操作。所以可以使用 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 组件内获取一个元素的真实 dom 结构的时候,可以使用原生方案 document 一套,也可以借助插件(没讲)。但是呢,vue 其实提供了一个方案,就是组件的 ref。一般获取真实 dom 节点用于获取某个值,并不是用于修改。
<button ref="btnDom">button>
this.$refs.btnDom;
// 就是获取了 button 的原生 dom
其实 ref 也可以直接写在组件上,那么获取的就是该组件实例,也就是说可以获取组件内的所有内容。进而也就可以实现父子组件之间的交互。
<li ref="ceShi"/>
console.log(this.$refs.ceShi.arr)
//this.$refs.ceShi 相当于该组件里面的this.arr
// 也可以调用子组件的方法
this.$refs.ceShi.方法名()
组件内使用 this 其实就是想要使用该组件下的 data props methods computed 自定义事件 ref … ,那么组件内我们通常使用的 this 就指的是该组件的实例(VueComponent)。
如何获取组件实例
组件从出现到渲染页面或者再页面中销毁,各个阶段 vue 都提供了对应的函数,供开发者使用。这些函数被称作生命周期钩子。生命周期钩子是同步函数。
初始渲染阶段
数据更新阶段
组件的销毁阶段
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
}
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 的切换
<component :is="currentComponentName"> component>
动态组件搭配 keep-alive 实现动态组件的数据缓存。允许组件有条件的缓存(include,exclude,max)
当动态组件切换的时候每个组件默认都会变成初始状态,假如有的组件内有 data 并且希望 data 修改的时候能够保留,意思就是动态组件切换的时候中间的某个组件的 data 不会被初始化。此时就需要使用 keep-alive
<keep-alive include="Home" exclude="Home" :max="10">
<component :is="currentComponentName"> component>
keep-alive>
还有一种更新title的方法:
父组件
<component :title.sync="title" :is="activeType">component>
子组件
<button @click="$emit('update:title','three')">threebutton>
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 被删除),在过渡/动画完成之后移除。
<transition appear name="fade">
<p class="tran-text" v-if="show">Hellop>
transition>
.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);
}
}
<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 name="fade1" mode="out-in">
<button v-if="!state" @click="state=true" key="on">onbutton>
<button v-else @click="state=false" key="off">offbutton>
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">
<li v-for="num in listNumber" :key="num">{{num}}li>
transition-group>
对于 vue 这种单页面应用,官方提供了 vue-router 库,来实现多页的效果。
如何实现
// 一 。创建页面路由
// 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");
<ul>
<li><router-link to="/">homerouter-link>li>
<li><router-link to="/about">aboutrouter-link>li>
ul>
<router-view>router-view>
快速创建路由方式(前提是 vue 的环境时 vue-cli3.0 以上)
vue add vue-router
快速安装之后,项目内就会自带一个路由 demo,直接使用即可
对于 router-view 和 router-link 组件的各种配置参考 vue-router 官方文档
此link激活时的类名,根据地址匹配,默认是包含匹配,想要精确匹配需要 exact 默认是true
,也有默认的类名:router-link-active
<li>
<router-link active-class="cc" exact to="/">homerouter-link>
li>
<li>
<router-link active-class="cc" exact to="/about">aboutrouter-link>
li>
<style>
.cc{
color:blue;
}
style>
路由跳转的时候会默认触发的一些函数,帮助开发者更好的实现路由的跳转。
一般写在main.js
详细流程请看文档
router.beforeEach((to, from, next) => {
// to 和 from 获取的就是组件内的 $route
console.log("全局前置守卫");
next();
if (to.path === "/" || login) next();
else alert("未登录,请先登录"), next("/");
// next 参数是一个函数 该函数的作用就是 通行
});
// 和全局前置守卫差不多,多数用上面的。
router.beforeResolve((to, from, next) => {
console.log("全局解析守卫");
next();
});
router.afterEach((to, from) => {
// 没有next函数
console.log("全局后置钩子");
});
// 单个用在路由中,不如组件内的守卫好用
{
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()
}
}
普通的
安装方式
npm i element
全局导入
// 在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(复制粘贴·)
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>
/* 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-loader 和 less, 基础用法和 scss 一样,这里用@,less和scss基本上一样
<style lang="less">
@color: red;
.less-demo {
h3 {
// color: teal;
color: @color;
}
}
</style>
// 需要安装 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>
// 直接安装 6 版本的swiper会出问题建议安装 5 版本
import Swiper from 'swiper'
// 当你想要引入 包 内的其他文件,路径直接写第三方包名 + / 找即可
import 'swiper/css/swiper.min.css'
// 引入 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
就是实现组件之间交互的终极方案。将组件间需要交互的数据(data),共享到 vuex 创建的 store(仓库) 内。
npm i vuex
// 一. 导入 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 内导入,并且在创建整个 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 内的数据写成计算属性的方式
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
}
})
}
const store = new Vuex.Store({
...
mutations: {
add(state){
state.count++
}
}
})
store.commit('add')
来修改 store 中的数据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",
}),
},
就像计算属性一样,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"]),
// {{ getNum(30) }}
...mapGetters({
res: 'sum',
limitNum: 'getNum'
})
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'])
}
// 把这些常量放在单独的文件中可以让你的代码合作者对整个 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 自带一个日志插件用于一般的调试,就是当 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
}
},
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 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
}
...
})
<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 的进阶阶段
局部混入
// 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('全局混入');
}
})
<div v-html="htmlStr">div>
data() {
return {
htmlStr: '123'
}
},
<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()
}
}
}
当全局过滤器和局部过滤器重名时,会采用局部过滤器。
<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 添加全局功能。
// 组件内,输出下面定义的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 方法会自动检测全局的你的插件是否注册没注册
创建函数式组件 两种方法 :
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">
<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('测试')
}
}
直接在 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标签里是可以套其它标签的)
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 引用
// 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服务
const res = await axios.get('http://localhost:3000/menus')
console.log(res.data.menuList)
// 写在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();
})
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)
// 传参
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
}
// 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的环境的一个工具
// 布尔
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
格式化函数时,函数名称或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])
}
}
参考网址https://v4.webpack.docschina.org/guides/getting-started/
基本安装和打包编译和配置按着网址写即可(npx webpack, 自动生成 main.js, 每修改每编译)
const path = require('path');
module.exports = {
// 入口,打包编译哪个文件(其它我们打包的是main.js)
entry: './src/index.js',
// 出口设置,编译完成之后的文件
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
// 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(此时页面就被打开,修改就不用编译了)
// 直接将css文件当做了模块导入到 index.js 内
// 默认只有 js 文件才会被当做模块处理
// 所以需要处理非 js 模块类型的文件,需要 loader
// 比如处理 css ,需要安装 css 对应的loader
import "./assets/style.css"
import pic from './assets/logo.png'
// 需要安装loader,只有使用变量的时候才可以
element.innerHTML = ``
// 点击可用选项 即可以查看更多,老师给选的是 eval-source-map
// 功能是报错行数显示的更加准确
devtool: 'inline-source-map',
// 没有模板:
plugins: [
// 应用上 HtmlWebpackPlugin 插件功能,自动创建 html 文件,你可以在使用插件的时候,传递一些页面的配置,此时可以删除 index.html
// 页面是自动创建的,里面基本上什么都没有,如果你想要以一个Html模板创建index.html,可以添加html-webpack-template属性设置
new HtmlWebpackPlugin({
title: '我的 webpack-demo'
})
],
// 有模板
plugins: [
new HtmlWebpackPlugin({
// 此时需要在dist下创建一个模板,名字任意
template:'./dist/index.html'
})
],
// 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')
// 使用 vue-loader, 需要下载 vue-loader之外 还要下载辅助的插件 vue-template-compiler
// 先 const,rules、plugins里也加东西
// -D 意思是 --save-dev
npm install -D vue-loader vue-template-compiler
module.exports = {
//...
devServer: {
// 控制台(console)显示消息:关闭
clientLogLevel: 'none'
}
};
module.exports = {
//...
devServer: {
// 当使用vue路由的时候,选择了history模式,在这个模式下,刷新非首页的页面,直接找不到 404,需要将该页面下的所有页面请求,全部指向到首页
historyApiFallback: true
}
};
// 先安装 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没生效,解决方法如下
// 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
```js
{
test: /\.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
```
```js
{
"presets": ["@babel/preset-env"],
"plugins": ["transform-vue-jsx"]
}
```
(display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;)
use: [
'style-loader',
'css-loader',
// 'postcss-loader', 等于以下写法
{
loader:'postcss-loader',
},
]
```js
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist:[
'Android 4.1',
'iOS 7.1',
'Chrome > 31',
'ff > 31',
'ie >= 8'
// 'last 2 versions'
],
// 网格布局
grid:true
})
]
}
```
entry: ['babel-polyfill','./src/main.js'],
注:在main.js中引入import ‘babel-polyfill’,解决路由兼容11 10 9 问题(实际上没出错!)
"scripts": {
// 生产环境
"build": "webpack --config webpack.prod.js",
// 开发环境
"serve": "webpack-dev-server --open --config webpack.dev.js"
}
// 运行生产模式的时候会自动创建到设置的 dist 里,其中有个about.vue懒加载。
component:()=>import('@/views/About')
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,
// 图片直接引用比如 ,会自动解析成模块,也就是不能使用,所以写下面这句代码
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/'
},
},
],
},
],
{
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
// webpack.config.js 中
module.exports = {
//...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/templates/')
}
}
};
// router下的index.js中,用@代替..(src)
import Home from '@/views/Home.vue'
// webpack.config.js 中
resolve: {
extensions: ['.js', '.json', '.vue']
},
// router下的index.js中,省略.vue
import Home from '@/views/Home'
resolve: {
// 先在src里查找,找不到再去node_modules中查找
modules: [path.resolve(__dirname, 'src'), 'node_modules']
},
// 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"
})
// 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()
]
},
}
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']
}
}
}),
],
}
};
const path = require('path');
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
const path = require('path');
output: {
// 加的js是文件夹的名字,hash 是处理缓存
filename: 'js/bundle.[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
// 按照上面的文档复制过来,配置写的是文件夹的名字
outputPath: 'fonts',
}
}
]
},
{
// 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
// 在开发环境中
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')
把代码打包到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/'
: '/'
}
npm install axios --save
npm install mockjs --save-dev
npm install json5 --save-dev // 解决json文件不能添加注释的问题(可有可无)
// 先导入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 文件名` 运行
{
id: "@id()",
username: "@cname()",
date: "@date()",
avatar: "@image('200x200','red','#fff','avatar')",
description: "@paragraph()",
ip: "@ip()",
email: "email()"
}
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);
module.exports = {
devServer:{
before:require("./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))
})
}
}
import axios from 'axios'
axios.get('/user/userinfo').then(res => {
console.log(res);
}).catch(err => {
console.error(err);
})
// 如果后台写好了,那么把 MOCK 值变为 false
MOCK = true
Mock.mock('/user/userinfo','get',{
id: "@id()",
username:"@cname()",
date:"@date()",
ip:"@ip()",
email:"@email()"
})
<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();
}
}
方便的使用方法:在 main.js 里导入axios,window.axios = axios, 页面中直接使用即可。
一百度就有,记住流程就可以。另外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)
});
文件必须放在public文件夹下,放别处都不生效,新名字的扩展名可有可无
<a :href="`${path}article.xlsm`" download="新名字.xlsm">下载本地模板a>
data() {
return {
path: process.env.BASE_URL // 这里指得是 public(/)
}
}