vue2

第一类引入方式

就是直接把vue的js代码库运行到html环境中

1.自己引入cdn

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

2.自己把vue.js文件放在项目文件夹中引入项目 然后webpack打包

import Vue from './vue.js'
new Vue({el: "#app",data: {msg: "hello",age:"18"}
})

3.编辑器直接生成cdn的方式

HbuilderX 创建项目 选择 vue项目普通模式

第二类引入方式

就是直接把vue的代码环境集成了 然后开始在vue的代码环境中写项目,最终才经过webpack打包生成第一类方式

  1. 自己构建vue的脚手架(面试): 用npm下载vue 引入到项目中 然后webpack打包

//第1.1种
// var Vue = require("vue")
​
//第1.2种 自己构建vue的脚手架
//这种方式需要vue的加载器去解析.vue文件,参考网址:https://blog.csdn.net/weixin_44867717/article/details/104447943
​
//index.js入口文件
import Vue from 'vue'
import MyCom from './App.vue'
new Vue({render(h){return h(MyCom)}
}).$mount('#app')
​
​
//先下载:npm install -D vue-loader vue-template-compiler
//webpack.config.js打包配置文件:
const webpack= require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin') //引入vue加载器
rules: [{test: /\.vue$/,loader: 'vue-loader'}]//loader
plugins:[new VueLoaderPlugin()]//插件
​
//项目配置文件 pakage.json文件中:
//scripts:{
//"dev":"webpack-dev-server --open --inline --progress --config webpack.config.js"
//}
//然后就可以启动了:npm run dev

2.使用官方脚手架的方式来构建项目环境(面试)

1.  cnpm install  @vue/cli -g //下载官方脚手架
2.  vue create app1  //项目名称
3. 接下来让你选择一些默认要生成的工具,不管直接回车
4. 进入项目文件夹: cd app1 //进入项目文件夹,也可以直接在项目文件夹中打开终端
5. 启动:
npm run serve  //生成的打包文件在内存中不会写入磁盘用于开发阶段
或者
npm run build //生成的打包文件在dist中 用于项目上线
​

3.可视化项目管理方式(面试)

1.  cnpm install  @vue/cli -g
2.  vue ui
3. 打开的界面 中
4.创建项目==>填写项目名字,选择项目创建的目录 点击创建
5.等待它下载所有配置文件完毕
6.任务中serve启动以后想当与启动了热更新服务器:默认localhost:8080(也可以点启动app直接帮我们打开localhost:8080)
7.如果把项目做完以后要上线,就启用build相当于webpack的打包,生成dist文件,注意:打包的路径是相对路径要改一下配置中的公共路径为:  ./ 然后点保存修改  

4.编辑器直接生成脚手架环境的方式 (做项目推荐)

HbuilderX 创建项目 选择 vue项目 vue-cli默认模板
然后要:npm i
再:npm run serve

//注意:代码中回车或者空格导致的报错 禁用eslint启用严格模式来加载我们的代码,项目下新建vue.config.js文件:

module.exports = {lintOnSave: false
}

//注意:npm run build打包后 生成的引入文件的路径不对 解决方案:

//第1种.手动把路径的斜杠删除

//第2种.在根目录下新建vue.config.js

复制代码:

module.exports = {/** 区分打包环境与开发环境* process.env.NODE_ENV==='production'  (打包环境)* process.env.NODE_ENV==='development' (开发环境)* baseUrl: process.env.NODE_ENV==='production'?"https://cdn.didabisai.com/front/":'front/',*/
​// 项目部署的基础路径
​// 我们默认假设你的应用将会部署在域名的根部,
​// 例如 https://www.my-app.com/
​// 如果你的应用部署在一个子路径下,那么你需要在这里
​// 指定子路径。比如将你的应用部署在
​// https://www.foobar.com/my-app/
​// 那么将这个值改为 '/my-app/'
​baseUrl: "./", // 构建好的文件输出到哪里
​outputDir: "dist", // where to put static assets (js/css/img/font/...) // 是否在保存时使用‘eslint-loader’进行检查 // 有效值: true | false | 'error' // 当设置为‘error’时,检查出的错误会触发编译失败
​lintOnSave: false, // 使用带有浏览器内编译器的完整构建版本 // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
​runtimeCompiler: false, // babel-loader默认会跳过`node_modules`依赖. // 通过这个选项可以显示转译一个依赖
​transpileDependencies: [/* string or regex */], // 是否为生产环境构建生成sourceMap?
​productionSourceMap: false, // 调整内部的webpack配置. // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
​chainWebpack: () => {},
​configureWebpack: () => {}, // CSS 相关选项
​css: {// 将组件内部的css提取到一个单独的css文件(只用在生产环境)
​// 也可以是传递给 extract-text-webpack-plugin 的选项对象
​extract: true, // 允许生成 CSS source maps?
​sourceMap: false, // pass custom options to pre-processor loaders. e.g. to pass options to // sass-loader, use { sass: { ... } }
​loaderOptions: {}, // Enable CSS modules for all css / pre-processor files. // This option does not affect *.vue files.
​modules: false}, // use thread-loader for babel & TS in production build // enabled by default if the machine has more than 1 cores
​parallel: require("os").cpus().length > 1, // PWA 插件相关配置 // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
​pwa: {}, // configure webpack-dev-server behavior
​devServer: {open: process.platform === "darwin",
​disableHostCheck: false,
​host: "192.168.2.57",
​port: 9000,
​https: false,
​hotOnly: false, // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#configuring-proxy
​proxy: null // string | Object
​// before: app => {}}, // 第三方插件配置
​pluginOptions: {// ...}
};
​
​
​

npm 下载指令 -S -D

//下载到项目依赖中 以后项目上线时 这个模块的代码也在项目中起作用(vue jquery等) npm i xx1 xx2 --save npm i xx1 xx2 --S//--save的简写

//下载到项目依赖中 以后项目上线时 这个模块的代码不在项目中(webpack css-loader vue/cli等) npm i xx1 xx2 --save-dev npm i xx1 xx2 --D//--save-dev的简写

VM对象和基础指令(面试)

1.普通插值表达式插入数据: 在标签尖括号中使用{{}}插入js表达式:变量,函数调用,三目运算等等 ,插值表达式中的标识符 代表vue对象中的data的属性名或者methods中的方法名

<h1>{{msg+"666"}}</h1>
<p>{{fn()}}</p>
<p>{{ count==10?"hellohqyj":"hqyjhello" }}</p>

2.文本指令:(面试) v-html ==>相当于innerHTML v-text==>相当于innerText v-pre==>插件表达式就被识别为文本,而不是js表达式 v-cloak==>加上这个属性的标签相当于在构建虚拟节点的时候就会有这个属性,等data的数据生成的时候,这个标签会自动去掉这个属性,可以利用这个特性来在css中把这个元素在加载初期写样式(隐藏)

示例:
<h1>{{msg+"666"}}</h1>
<p v-cloak>{{fn()}}</p>
<p v-cloak>{{ count==10?"hellohqyj":"hqyjhello" }}</p>
​
<div v-html="p_html"></div>
<div v-text="p_text"></div>
<p v-pre>{{p_pre}}</p>

3.考点:如何解决vue第一次加载的时候 页面上使用的数据会闪烁?(面试)

3.1.界面加载的时候会把节点直接挂载到文档树中,导致{{msg+"666"}}这个字符串会显示一下
3.2.vue对象生成data数据时候 回去刷新界面 把{{msg+"666"}}字符串替换成结果字符串
3.3.导致界面第一次加载的时候会闪屏
3.4.解决方案:使用v-html,v-text指令操作,或者css中加[v-cloak] {display:none};

4.给元素绑定属性 所有标签中属性绑定js中变量: 标准写法: v-bind:src="变量" 简写形式: :src="[10,20,30]"

示例:
<a v-bind:href="link">baidu</a>
<a :href="link">baidu</a>
<img v-bind:src="myimg">
<img :src="myimg">

事件

1.方法的写法 在methods中写方法,供事件或者别的方法内部调用 方法的写法:由于是做了es6语法处理的 所以学过的所有方式的写法都行

function fn4 () {console.log("fn4")}
​
var fn5=()=>{console.log("fn5")}
​
var fn6=function(){console.log("fn6")}
​
new Vue({el:"#app",data:{},  methods:{fn1(){console.log("fn1")},fn2:function(){console.log("fn2")},fn3:()=>{console.log("fn3")},fn4,fn5,fn6                   }
})

2.事件绑定

//v-on: 和  @ 都是绑定事件的指令
//指令后面跟事件类型,值就是methds中的方法,可以加小括号也可以不加
<button v-on:click="fn1()">点击事件1</button>
<button @click="fn2">点击事件2</button>
​

3.事件修饰符(面试)

  • .stop 阻止冒泡,阻止从当前元素经过的所有冒泡行为

  • .prevent 阻止默认事件

  • .capture 添加事件侦听器时让事件在捕获阶段触发

  • .self 其他元素的事件触发时 事件链经过它,无论是捕获还是冒泡阶段都不会触发它的事件,只有它自己是精准对象才会触发事件, 虽然它自己不会被别人影响,但是它自己的事件触发的时候还是会生成事件链经过其他元素,并不阻止继续冒泡到其他元素

  • .once 事件只触发一次,触发完之后,事件就解绑

  • 多修饰符一起使用:连点

<div class="box" @click="divBoxHandler"><input type="button" @click.stop="btnHandler" value="戳他">
</div>
​
<a v-on:click.prevent.once="doThat">阻止点击跳转,但是只会阻止第一次</a>

4.事件中的this与数据操作(面试)

1. 方法中的this代表vm对象
a.方法和ES5的函数中的this是vm对象
b.ES6的箭头函数中的this就不是vm==>因此推荐事件的函数采用ES6的对象的方法写方法 这种写法2. 操作数据: this.xx="新值"
//这里的修改会执行两步操作:
//a.修改内存data容器中的数据
//b.刷新UI==>重新设置innerHTML//后面学的差不多了回头补充:高薪面试题 底层设计:js的语法 definexxx 设计模式:发布订阅

样式绑定

(1) 对class 属性进行绑定

<!--对象语法,v-bind:class 指令也可以与普通的 class 属性共存-->
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
<div v-bind:class="classObject"></div>
​
对应js 中的data:
data: {isActive: true,hasError: false,classObject: {active: true,'text-danger': false}
}
​
​
<!--数组语法,这样写将始终添加 errorClass,但是只有在 isActive 是真值时才添加 activeClass-->
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
​

(2)对style 进行绑定

<!--对象语法-->
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
​
对应js 中的data:
data: {activeColor: 'red',fontSize: 30,red1:"red",styleObject: {color: 'red',fontSize: '13px'}
}
​
<!--数组语法,可以将多个样式对象应用到同一个元素上-->
<div v-bind:style="[baseStyles, overridingStyles,{color:red1}]"></div>
​
​

数据绑定(面试)

响应式数据:只能由代码改变UI或者只能由UI改变代码 双向数据绑定:代码改变UI,UI也能改变代码

双向数据绑定的实现: 2种方式 1.自己实现,vue可以自己实现(没必要) 微信开发可以自己实现(只能自己实现) 利用input事件,用户交互的时候,获取用户输入的值,然后把值绑定到data容器中

2.系统指令:v-model

//vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。//利用 Object.defineProperty 自己实现双向数据绑定
<body><div id="app"><input type="text" id="txt"><p id="show"></p></div>
</body>
<script type="text/javascript">var obj = {}Object.defineProperty(obj, 'txt', {get: function () {return obj},set: function (newValue) {document.getElementById('txt').value = newValuedocument.getElementById('show').innerHTML = newValue}})document.addEventListener('keyup', function (e) {obj.txt = e.target.value})
</script>

vue简介

Vue.js是什么? 是阿里的尤雨溪出的框架

  • Vue.js(读音 /vjuː/, 类似于 view)是目前最流行的一个框架,React是最流行的一个框架。

  • Vue.js是前端的主流框架之一,和Angular.js、React.js并称为前端三大主流框架。

    数据驱动

    vue.js 数据驱动和组件化开发,轻量级一些,分层渐进式框架;spa React.js 数据驱动和组件化开发,灵活性很高,需要什么都得自己构建逻辑自己写;app Angular.js 数据驱动 1.0 ,加上了组件化开发2.0,重量级框架;大型企业OA办公

  • Vue.js 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。

  • Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和Vue生态系统支持的库开发的复杂单页应用。

  • Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

特点:(面试) 1、三大主流框架的区别?设计思想和使用场景

2、vue.js 渐进式框架(分层的设计模式)是什么意思?五层设计

3、vue.js 的核心是什么?数据驱动(响应式数据)和组件化开发

//面试题: 渐进增强 优雅降级

MVC和MVVM(面试)

MVC与MVVM之间的区别

(1)MVC

在实际应用开发场景中,开发者常用的一种设计模式是MVC(eg:node(后端)中的MVC设计模式):

  • M(Model):数据模型层。是应用程序中用于处理应用程序数据逻辑的部分,模型对象负责在数据库中存取数据。

  • V(View):视图层。是应用程序中处理数据显示的部分,视图是依据模型数据创建的。

  • C(Controller):控制层。是应用程序中处理用户交互的部分,控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

(2)MVVM

  • M(Model):模型层。就是业务逻辑相关的数据对象,通常从数据库映射而来,我们可以说是与数据库对应的model。

  • V(View):视图层。就是展现出来的用户界面。

  • VM(ViewModel):视图模型层。就是与界面(view)对应的Model。因为,数据库结构往往是不能直接跟界面控件一一对应上的,所以,需要再定义一个数据对象专门对应view上的控件。而ViewModel的职责就是把model对象封装成可以显示和接受输入的界面数据对象。

条件渲染(面试)

v-if /v-else 或者 v-show

        <div id="app"><div v-if="flag">666</div><div v-show="flag">777</div></div><script type="text/javascript">new Vue({el: "#app",data: {flag:false}})
//1.使用的变量为true就显示,false就隐藏
//2.在业务中常常可以通过操作if或者show使用的变量,来达到操作元素显示和隐藏的效果
//3.v-if的做法是删除节点,v-show做法是操作css的display:none
​
visibility: hidden; 不脱离文档流的
display:none        脱离文档流
v-if                删除节点
v-show              display:none
​
//4.这个两个谁好?(面试)
//根据它们底层的设计不一样有各自的使用场景
// v-if具有较高的 切换消耗,常常用在用户不常切换的模块
// v-show具有较高的性能消耗,常常用在频繁切换的模块中     

循环渲染(面试)

1.for和if放在了同一个标签中 没有先后顺序的要求,但是先执行for

渲染过程为:对arr每一项先做map循环判断v-if给出的条件,再做一遍for 循环渲染

这样引起的问题是:arr 数组新增一项数据时,会对每一项再做一遍v-if 循环,然后for 循环渲染

2.解决方案把for弄到最外层(面试)

如果if和for套在一层,数据容器发生变化时,if会重新判断一遍 嵌套的写法 数据容器变化时 if只判断新增的数据 这样当arr 数组某一项数据发生变化时,只对新增的数据进行v-if 判断,节约渲染效率

这样又会产生新的问题:外层for的div会也创建一个挂载到DOM中 解决方案:wx采用的是block元素 vue呢? template 其实就是dom操作中的fragment

<div v-for="(item,index) in arr2" :key="item.id"><div class="box" v-if="item.age>=18"><p>{{item.name}}</p><p>{{item.age}}</p><p>{{item.info}}</p></div>
</div>

3.key的意义(面试)

data中for循环的容器数据个数发生变化时,会跟for中的vm节点个数作比较

如果数据多了,会在vm节点后面增加对应数量的节点,并不会重新创建所有节点,然后vm去更新对应的DOM

然后就去刷新数据到界面: 按照for的数据容器中的数据顺序来渲染如果用户以前操作过旧节点,那么新数据顺序可能会出现跟旧节点顺序不匹配的效果(旧节点跟旧数据没有对应起来)

解决方案: for循环时把数据跟创建的节点利用给元素绑定唯一key值

简答:因为vue在刷新页面组件时,会把旧节点跟新vm节点做比较,如果要增加节点,并不会删除旧节点,而是复用 这样会导致节点跟数据没有绑定关系而重新渲染,用key可以将数据与节点绑定起来

       <div id="app"><h1>活动:1分钟内从1万件商品中选择你喜欢10件送给你</h1><button type="button" @click="add">加载新的商品</button><button>提交</button><div><div v-for=" (item,index) in arr" :key="item.id"><input type="checkbox" name="goods"/>  {{item.name}}=={{item.info}}</div></div></div><script type="text/javascript">new Vue({el:"#app",data:{arr:[{id:10001,name:"鞋子1",info:"这个鞋子好1"},{id:10002,name:"鞋子2",info:"这个鞋子好2"},{id:10003,name:"鞋子3",info:"这个鞋子好3"},{id:10004,name:"鞋子4",info:"这个鞋子好4"}],count:5},methods:{add(){//网络请求 然后把新商品添加到arr中//假装网络请求了新数据 var result=this.count++              this.arr.unshift({id:result,name:"鞋子"+result,info:"这个鞋子好"+result})}}})</script>         

过滤器(面试)

filter主要用于数据展示之前的处理 过滤器只能用在v-bind或者插值表达式中 语法:

<div id="app"><div v-for="(item) in sinaArr" class="box"><p v-html="item.user.name"></p><img :src="item.user.img" ><p v-text="item.text"></p><!-- <p>发表时间:{{item.create_At|timerTool}}</p> --><!-- 下面这种写法是不行的:过滤器只能应在v-bind或者插值表达式中 --><p v-html="item.create_At|timerTool">发表时间:</p></div>
</div>new Vue({el:"#app",data:{sinaArr:[{create_At:"Thu Apr 16 14:08:18 +0800 2020",text:"内容1",user:{name:"karen1",img:"1.jpg"}},{create_At:"Thu Apr 16 14:01:18 +0800 2020",text:"内容2",user:{name:"karen2",img:"2.jpg"}},{create_At:"Thu Apr 16 10:39:18 +0800 2020",text:"内容3",user:{name:"karen3",img:"3.jpg"}},{create_At:"4.jpg"}}]},filters:{timerFormat(arg){var dt1=new Date(arg)var dt2=new Date()var abstime=dt2-dt1if(0<=abstime&&abstime<1000*60){return "刚刚"}else if(1000*60<=abstime&&abstime<1000*60*60){var m=new Date(abstime).getMinutes()+""return `${m}分钟前`}else if(1000*60*60<=abstime&&dt1.getDate()==dt2.getDate()){var m=dt1.getMinutes()+""var h=dt1.getHours()return `今天 ${h}:${m.padStart(2,"0")}`}else if((dt1.getDate()+1)==dt2.getDate()){var m=dt1.getMinutes()+""var h=dt1.getHours()return `昨天 ${h}:${m.padStart(2,"0")}`}else if((dt1.getDate()+2)==dt2.getDate()){var m=dt1.getMinutes()+""var h=dt1.getHours()return `前天 ${h}:${m.padStart(2,"0")}`}}}}

计算属性(面试)

把computed中的方法当做属性使用,会返回一个数据供使用:

new Vue({el:"",//关联界面元素data:{},//vm的数据源methods:{},//方法filters:{qq(){}},//过滤器computed:{xx(){}} //xx就是一个计算属性})

使用示例:

<div id="app"><p>{{msg}}</p><p>方法获取的年龄:{{getAge()}}</p><p>计算属性获取的年龄:{{getAge_computed}}</p><button @click="change">改变birth的值看看年龄变不变</button></div>
new Vue({el: "#app",data: {msg: "hello",birth: "1995-02-03"},methods: {getAge() {var age = new Date().getFullYear() - new Date(this.birth).getFullYear()return age + "岁"},change() {this.birth = "1996-02-03"}},computed:{//计算属性第一种用法getAge_computed(){var age = new Date().getFullYear() - new Date(this.birth).getFullYear()return age + "岁"}//计算属性第二种用法xx:{set(oldvalue){},get(){}}}})

计算属性和方法的区别:(面试) 计算属性会把使用到的data中的属性缓存起来,防止页面发生大量重复计算, 提升js 运行效率,如果计算属性中使用到的data中那部分数据变了才会重新调用计算属性 methods方法没有计算结果缓存起来,data任何数据发生改变,方法都会被重新调用一遍 方法常常是作用的事件使用,计算属性常常是动态计算结果时使用

补充:关于计算属性 函数什么情况下调用

//计算属性使用时当做属性使用
//计算属性设计时当做函数设计(就像es6中的属性)
//当计算属性的函数中使用到的data中的数据发生变化时,计算属性就会重新执行并刷新UI
//1.如果是修改了data中监听的某个的属性值 计算属性就会运行
//2.如果是修改了data中监听的某个属性值内部的数据,计算属性就不会重新运行
//比如:计算属性使用的是data中的一个数组,某个交互把数组内部的某个下标的值改了,但是这个数组没有改变,就不会触发计算属性
//3.解决2的办法1:把修改后的数组重新赋值给data,让引用发生变化,来触发计算属性
//3.解决2的办法2:赋值 JSON.parse(JSON.stringfy(data))
methods:{ change(arg,index1){this.arr[index1]=argthis.arr=[...this.arr]}
}
computed:{total(){//eval(this.arr.join("+"))//  eval("100+200+88")==>把字符串当做代码运行 产生运算结果return  eval(this.arr.join("+"))}}

属性监听器(面试)

watch:{x(){}}中的方法名必须跟要监听的data中的属性名一样,才代表监听指定属性

当侦听器监听的属性发生变化时,就会调用watch中对应的方法

侦听器属性,比计算属性计算效率消耗大

new Vue({el:"",//关联界面元素data:{x:12},//vm的数据源methods:{},//方法filter:{},//过滤器computed:{xx(){}}, //xx就是一个计算属性watch:{x(){}} //x就是监听了data中的x属性的一个监听器
})

自定义指令(面试)

除了默认设置的核心指令( v-model 和 v-show 等),Vue 也允许注册自定义指令。

在Vue里,代码复用的主要形式和抽象是组件。

然而,有的情况下,仍然需要对纯 DOM 元素进行底层操作,这时候就会用到自定义指令 。

以一个input元素自动获得焦点为例,当页面加载时,使用autofocuse可以让元素将获得焦点 ,但是autofocuse在移动版Safari上不工作,现在注册一个使元素自动获取焦点的指令。

指令注册类似于组件注册,包括全局指令和局部指令两种。

1)全局指令

// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {// 当绑定元素插入到 DOM 中。inserted: function (el) {// 聚焦元素el.focus()}
})

2)局部指令

var vm = new Vue({el: '#app',directives:{focus:{inserted: function (el) {el.focus()}      }}
})

在模板中任何元素上使用新的 v-focus 属性

<div id="app"><input v-focus>
</div>

3)钩子函数

指令定义函数提供了几个钩子函数(可选) 。

【bind】

  只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

【inserted】

  被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。

【update】

  所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。

【componentUpdated】

  所在组件的 VNode 及其孩子的 VNode 全部更新时调用。

【unbind】

  只调用一次, 指令与元素解绑时调用。

注意区别:

bind与inserted:bind时父节点为null,inserted时父节点存在;

update与componentUpdated:update是数据更新前,componentUpdated是数据更新后。

4)钩子函数参数

【el】

  指令所绑定的元素,可以用来直接操作 DOM。

【binding】

  一个对象,包含指令名称及该指令所绑定的表达式信息。

【vnode】

  Vue 编译生成的虚拟节点。

【oldVnode】

  上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

注意:除了 el 之外,其它参数都是只读的,尽量不要修改他们。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

5)函数简写

大多数情况下,可能想在bind和update钩子上做重复动作,并且不想关心其它的钩子函数。可以这样写:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>Vue.directive('demo', function (el, binding) {console.log(binding.value.color) // => "white"console.log(binding.value.text)  // => "hello!"
})

for写一个新浪微博

        <style type="text/css">.box{position: relative;width: 970px;min-height: 200px;background-color: rosybrown;border-radius: 10px;/* margin-top: 10px; */margin: 10px auto;}</style><div id="app"><button @click="fn">加载</button><div class="box" v-for="(item) in arr"><img :src="item.user.profile_image_url" ><h1>{{item.user.name}}</h1><p>{{item.created_at|timerFormat}}发表</p><p>{{item.text}}</p><div><img v-for="(imgObj) in item.pic_urls" :src="imgObj.thumbnail_pic"></div> <div class="bottom">评论:({{item.comments_count}})</div></div></div><script type="text/javascript">new Vue({el:"#app",data:{arr:[]},methods:{fn(){// console.log($)// let url="https://api.weibo.com/2/statuses/home_timeline.json?access_token=2.00ZmCkcDlel8HDd856f9b9ccsVwsYD&callback=?"// $.getJSON(url,(result)=>{//    console.log(result)// }) // var result=sinadataconsole.log(sinadata)this.arr=sinadata.statuses}},filters:{timerFormat(arg){console.log(arg)var sinaTimer=new Date(arg)var nowTime=new Date()var absTimer=nowTime-sinaTimerif(0<=absTimer&&absTimer<=1000*60){return "刚刚"}else if(1000*60<absTimer&&absTimer<=1000*60*60){var dt=new Date(absTimer)return dt.getMinutes()+"分钟前"}else if(1000*60*60<absTimer&&absTimer<=1000*60*60*24){return sinaTimer.getHours()+":"+sinaTimer.getMinutes()}else if(1000*60*60*24<absTimer&&absTimer<=1000*60*60*24*2){return "昨天--"+sinaTimer.getHours()+":"+sinaTimer.getMinutes()}}}})</script>

网络

1.具体的网络请求的工具的用法见node阶段视频

注: egg后端的缓存,要求前端的网络请求运行携带缓存cookie数据,axios网络请求携带参数下面两种方式:

①axios.defaults.withCredentials=true;//让ajax携带cookie

②axios.post(url,{pwd:123},{ withCredentials: true },(res)=>{})

axios.get(url,{params:{pwd:123},{ withCredentials: true },(res)=>{})

后端egg中配置:

//设置允许哪些源可以跨域访问当前服务器

// credentials: true 跨域服务的页面去做cookie缓存

config.cors = {

origin: 'http://ip:8080',

credentials: true//后端会给去前端返回缓存数据包 告诉浏览器 去做缓存

}

2.网络请求工具的二次封装:

2.1 原理: 把用到的axios等网络请求工具封装成自己的工具 在项目中使用,防止axios出问题时,快速的更换工具 不会影响项目代码

2.2.设计:

xx.prototype.myaxios=axios

xx.myaxios.post=axios.post

xx.myaxios.get=axios.get

xx.myaxios.put=axios.put

3.代理

//main.js
module.exports={lintOnSave:false,devServer:{port:"10086",host:"localhost",proxy:{"/":{target:"http://192.168.2.105:7001",changeOrigin:true,pathRewrite:{"^/":""}}}}
}

生命周期函数(面试)

生命周期: vm对象从创建到销毁的过程

vm对象的生命周期函数(钩子函数,生命钩子)

相当于是一种特殊事件,当vm实例在整个运行的过程中,会在不同的时期去执行特定的函数,这样的函数就是vue的生命周期函数 beforeCreate created beforeMount mounted destory/x这些钩子都只执行一次 beforeUpdate updated第一次构建不会调用,以后每次data被更新了就会调用 beforeDestroy destroyed 销毁的方式有两种:用户关闭和代码this.$destroy()//销毁vm实例

为什么要用生命周期函数? 0.把整个运行期间的业务区分的很明显 1.能够更好的帮助我们把产品的业务逻辑实现了 2.更有利于我们维护产品 和 修改需求 3.能够让我们写出更高质量的产品的代码 ​ ​ ​ 网络请求应该在什么钩子中,为什么? ​ 可以放在data生成以后更新数据之前 的所有钩子中 具体的更具业务需求来 常见的放在created或者mounted中 ​ 因为网络请求下来的数据 常常会存在data容器中 created:因为有时候我们希望异步的网络请求和vm的挂载同时进行 体现出CPU多核的优势 mounted:因为有时候我们希望本地的UI骨架已经加载完毕以后再去请求数据刷新UI ​ ​ 你用销毁钩子做过什么? 常常去把一些运行着的代码停下来 本地或者网络请求来记录用户的配置信息或者偏好设置

                //vm实例创建中//1.data都还没有构建出来数据//2.方法也还没有构建出来beforeCreate() {console.log("vm实例创建中")// console.log(this.msg)//这时候data都还没有构建出来数据// this.fn()},//vm实例创建完毕//data 方法 等等工具已经构建完毕//但是vm还没有挂载到界面上去,可以在这个钩子中做页面第一次加载时的网络请求created() {console.log("vm实例创建完毕")// console.log(this.msg)// this.fn()},//挂载到DOM之前//data等vm对象的工具构建完毕 正在挂载到DOM中beforeMount() {console.log("挂载到DOM之前")},//挂载到DOM上了//有点像window.onload//这里也可以做网络请求,页面加载了之后的所有业务,都可以在这里//这个函数执行标志着 vm和dom成功关联==>随意操作vm来间接的操作dommounted() {console.log("挂载到DOM上了")},//更新数据之前beforeUpdate() {alert("更新数据之前")debugger//卡住页面},//更新数据完毕//data.xx=100//xxx.innerHTML=data.xx//更新的是data,这时候还没有刷新UI,它会找一个合适的时机去刷新UI//这个钩子调用之后才会刷新UIupdated() {alert("更新数据完毕")},//销毁实例之前//常常去把一些运行着的代码停下来//本地或者网络请求来记录用户的配置信息或者偏好设置beforeDestroy() {console.log("销毁实例之前",this.msg)},//实例销毁了//钩子调用后 才销毁  做最后的挽救//从技术的角度来说  可以在这个方法打开其他程序,然后销毁这个程序destroyed() {console.log("实例销毁了",this.msg)}

组件(面试)

组件是可复用的 Vue 实例,主要用于开发中 具有相同特征不同数据的模块 把它集成为一个组件 供重复利用

组件基础(面试)

1.全局组件: 组件的属性不能用大写字母

组件的名字可以用驼峰命名法,但是使用的时候必须用连字符 全局注册的组件使用时不能使用单标签(不会报错,但是只能使用一次 多次使用只显示第一个)

注册的组件不要跟系统标签同名

2.局部组件: 一个vm实例可以有多个局部组件,但是只能供当前vm实例使用

3.单文件组件:

引入:@1官方脚手架 @2挂载vm对象 @3组件引入并渲染到vm中

单文件组件也有全局组件和局部组件 只是把一个组件单独写在一个.vue文件中,供别的组件引入然后注册 引入文件时:一般使用相对路径 上一下用../ 同级使用./ 下级使用/ @ 代表src文件夹

<div id="app"><nav1 son-proprety="子组件使用时属性传进去的值"></nav1>   <content1 img2src="./img/2.png"></content1></div><div id="app2"><nav1 son-proprety="子组件使用时属性传进去的值2"></nav1><content1 img2src="./img/2.png"></content1>  </div><script type="text/javascript">//祖册全局组件Vue.component("nav1",{data(){return {sondata:"子组件的数据"}},template:`<div><h1>{{sondata}}</h1><p>{{sonProprety}}</p></div>`,props:["sonProprety"]})// let content1={//            data(){return {img1:"./img/1.png"}},//            template:`<div><img :src="img1"/><img :src="img2src"/></div>`,//            props:["img2src"]//       }new Vue({el:"#app",data:{},components:{// content1//注册局部组件content1:{data(){return {img1:"./img/1.png"}},template:`<div><img :src="img1"/><img :src="img2src"/></div>`,props:["img2src"]}}})new Vue({el:"#app2",data:{}             })</script>

4.组件的属性 属性有两种写法:简单声明和详细描述:

4.1.简单声明 props:["prop1","prop2"]

4.2对属性做详细的描述

props: {propA: Number,     // 基础的类型检查 (`null` 匹配任何类型)propB: [String, Number],    // 多个可能的类型propC: {  type: String,required: true // 必填的字符串},propD: { type: Number,default: 100   // 带有默认值的数字},propE: {   type: Object,   // 带有默认值的对象或者数组填Arraydefault: function () { // 不建议直接填对象(因为对象直接量会一直占用内存),一般使用工厂函数,调用时才创建对象节省资源(面试)return { message: 'hello' }}},propF: {validator: function (value) {// 自定义验证函数返回为true就代表数据符合我们规定return ['success', 'warning', 'danger'].indexOf(value) !== -1}}}

5.v-slot: 插槽, 具名插槽 slot,slot-scope过时了 2.6.0使用v-slot 语法:v-slot:插槽名 语法糖:#插槽名 没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot中的数据 插槽名不用使用引号引起来,直接写变量名 插入的内容必须是template标签或者组件 不能是原生的元素

//设计组件: .vue组件文件中
<template>
<div class="content1"><slot></slot><slot name="slot1"></slot><h1>{{contentData.title}}</h1><h2>{{contentData.dt}}</h2><slot name="slot3">你不给我数据到3号插槽中  我就会默认显示出来</slot><p>{{contentData.text}}</p><slot name="slot2"></slot></div>
</template><script>export default{props:{contentData:{type:Object,default:()=>{return {title:"0",dt:"0",text:"0"}}}}
}
</script>//使用组件: .vue页面文件中
<template>
<div>  <content2 :contentData="arr[1]"><template #slot1><img src="../assets/28.jpg"></template><template #slot2><p>我在外部插入插槽的数据,不是子组件中的数据,也不是属性传进去的数据</p></template><template v-slot:slot3>666</template>         <p>我并没有指定插入到哪里</p></content2>
</div>
</template>

6.组件的嵌套: .vue文件既可以是一个页面,也可以是一个组件 它可以被别人.vue文件引入 然后作为组件使用

补充:组件的自定义事件和原生事件

//1.在原生组件(就是html标签)中  事件是由系统来设计触发条件的:
<div @click="fn">点我</div>//2.在自定义组件中,事件是由自己来设计什么时候触发的://绑定事件:
<mydiv @myclick="fn">点我</mydiv>//事件设计:
//在mydiv组件内部,你可以在你想要的条件下去触发事件
this.$emit("myclick","要给触发的事件的函数传入值")//这个代码放在你想触发自定义事件的地方//3.如果希望组件绑定原生事件(事件的触发条件设计由系统设计)
//给事件绑定事件修饰符   .native
<mydiv @click.native="fn">点我</mydiv>//事件名必须是系统存在的事件//4.自定义事件的sync   事件名和属性名相同时可以简写<MySon :title="msg" @update:title="fn"></MySon><MySon :title.sync="msg" ></MySon>

7.面试题(组件基础)

组件中的基础语法常见的面试题方向:

7.1 @ 是一个关键字,在引入的文件路径中 它代表src目录

7.2 template:组件的模板中只能有一个根节点

7.3 v-slot: 插槽, 具名插槽 slot,slot-scope过时了 2.6.0使用v-slot 语法:v-slot:插槽名 语法糖:#插槽名 没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot标签的尖括号中的数据 插槽名不用使用引号引起来,直接写变量名 插入的内容必须是template标签或者组件 不能是原生的元素

7.4 组件的data为什么是个函数然后返回对象,以前使用vm时都是个对象

组件和挂载到界面的vm对象的区别,vm挂载到页面上时,触发了钩子函数的,data生成了,页面上使用的数据就是data容器中渲染上去的,而且页面只有一个vm对象,所以的vm生成完毕(mouted)时data必须存在

组件是引入和注册以后不一定使用的,比如for循环0次就是组件对象生成了的,但是使用0次,所以组件对象并没有使用自己的data容器去渲染数据,造成资源浪费,解决方案就是懒加载:当使用data时去调用,才生成data对象

组件的data 设计成function的用义:组件可以多次使用,每使用一次,函数被调用一次则创建出不同的数据对象,实现同名组件的数据可以相互独立

7.5 scoped: style标签的scoped="scoped" 生成css的使用作用域只有当前组件内部的选择器生效

组件高级(面试)

7.传值

7.1父组件通过属性给子组件传值: 子组件的props接受数据

在页面模板中 使用变量名:属性 data 计算属性(重点)

//注意属性传值是单向的

7.2 子组件通过调用父组件的方法给父组件传值:子组件的自定义事件中,用$emit触发事件调用父组件方法给父组件传值 (重点)

因为通过属性传值是单向的,有时候我们需要子组件的data 数据需要交给父组件使用: 通过在子组件上定义自定义事件,在子组件中通过$emit 来触发事件;子组件的事件被触发并传参,事件处理函数可以接收到子组件的数据;事件绑定的事件处理函数在父节点上,故可在事件处理函数中用到子组件的数据值来修改父节点的数据。

//父组件中:
<my-search @myevent="handleEvent"></my-search>
//myevent是事子组件的自定义事件
//handleEvent是绑定的父组件的方法子组件中:
在任意业务中触发事件:this.$emit("myevent","要给父组件传的数据")
  1. 3多层组件传值:$listeners/$attrs(了解)

在不用状态管理vuex的时候,如何让GrandFather与Son通信,我们可以用可以emit一层一层的传递:会显得冗余。 vue2.4之后,提出$attrs、$listeners ,可以实现跨级组件通信。 $listeners官网解说:事件传递 $attrs官网解说:属性传递

在组件中绑定 可以把当前组件的自定义属性和方法传给子元素使用:

one组件:<two v-bind:xx="100" v-on:twoEvent="fn"></two>
two组件中:<three v-bind="$attrs" v-on="$listeners"></three>
three组件:可以访问two的 属性和触发事件: {{this.$attrs.xx}} this.$emit("twoEvent",20)
  1. 4 $ parent/$root、$children/$refs(了解)

这些功能都是有劣势或危险的场景的,官方建议我们尽量避开它们,但是高级点的面试题中会出现

 $root: 访问根组件vm对象,所有的子组件都可以将这个实例作为一个全局 store 来访问或使用,现在有更好的技术vuex代替。$parent:访问父组件对象,直接操作父组件的data数据,不需要再使用属性传值,但是容易出现渲染混乱之后只渲染一个的情况$children:访问子组件对象数组,不能保证顺序,也不是响应式的$refs:只会在组件渲染完成之后生效,并且它们不是响应式的。你应该避免在模板或计算属性中访问 $refs。//在组件或者原生元素绑定ref属性(类似于id):<myinput ref="myInput1"></myinput><input ref="myInput2"></input>//在父组件中可以通过 this.$refs访问到它:methods: {focus: function () {this.$refs.myInput2.focus()}}
  1. 5 中央事件总线bus(了解) event bus

通过创建一个新的vm对象,专门统一注册事件,供所有组件共同操作,达到所有组件随意隔代传值的效果

//vue-bus.js文件
const install = function (Vue) {const Bus = new Vue({methods: {emit(event, ...args) {this.$emit(event, ...args);},on(event, callback) {this.$on(event, callback);},off(event, callback) {this.$off(event, callback);}}});Vue.prototype.$bus=Bus;//由于这个新的vm放在与界面绑定的那个vm的原型上,因此页面上的所有组件都能通过this.$bus访问这个新vm对象
};
export default install;//main.js文件
import VueBus from './vue-bus'
Vue.use(VueBus);//组件文件中:
任意业务中都可以通过调用来绑定事件,触发事件并传值,和销毁事件
this.$bus.on(event,callback)
this.$bus.off(event,callback)
this.$bus.emit(event, ...args)示例:
组件1:this.$bus.on('changedFormObject',(val) =>{//接受并处理传过来的值:valthis.msg = val;});组件2:
this.$bus.emit('changedFormObject',this.inputValue);//把组件2的data中的给inputValue值传给组件1

7.6 Vue 依赖注入 - Provide/Inject(重点)

通常情况下,组件向组件传递数据,可以采用父子props层层传递,也可以使用busVuex直接交互。在Vue2.2.0之后,Vue还提供了provide/inject选项

官网不建议在应用中直接使用该办法,理由很直接:他怕你"管不好"

//爷爷
<template><div><p>{{ title }}</p><son></son></div>
</template>
<script>import Son from "./son"export default {name: 'Father',components: { Son },// provide选项提供变量provide: {message: 'provided by father'},data () {return {title: '父组件'}},methods: { ... }}
</script>//爸爸
<template><div><p>{{ title }}</p><grand-son></grand-son></div>
</template>
<script>
import grandSon from "./grandSon "
export default {name: "Son",components: { grandSon },data () {return {title: '子组件'}},
};
</script>
//孙子
<template><div><p>message:{{ message }}</p></div>
</template>
<script>
export default {name: "GrandSon",inject: [ "message" ],data () {return {title: '孙组件'}},methods: { ... }
};
</script>
  1. 7仓库vuex(重点)

后面讲

8.动态组件

有的时候,我们希望页面中的某个地方,在不同组件之间进行动态切换,这时候除了条件渲染,还可以使用动态组件

component 标签的 is属性语法:is后跟组件的变量名决定使用哪个组件来渲染

<component is="Sinabox"></component>==><Sinabox/>

<component v-bind:is="mycomponent"></component> 注意: is是组件名 :is是data中的变量中保存的组件名

<template><div id="app"><button @click="fn">xx</button><components :is="com"></components></div>
</template>
​
<script>
import HelloWorld from './components/HelloWorld.vue'
import HelloWorld2 from './components/HelloWorld2.vue'
export default {name: 'app',data(){return{flag:true,com:"HelloWorld",}},components: {HelloWorld,"HelloWorld2"},methods:{fn(){this.flag=!this.flagthis.com=this.flag?"HelloWorld":"HelloWorld2"}}
}
</script>

9.缓存组件keep-alive 动态组件的切换,切换后是不会缓存之前被切换掉的组件的,每次切换新组件的时候,Vue 都创建了一个新的组件对象。 有时候我们希望在A组件时用户做了一些操作,切换B组件时做了一些操作,当切回A组件时希望记住A的操作,不要重新创建A组件,keep-alive可以缓存动态切换的组件 <!-- 失活的组件将会被缓存!--> <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive>

<!-- 提供有include 和exclude 属性决定哪些组件可以被缓存(字符串或正则表达式)。!-->

<keep-alive :include="/a|b/"> <component v-bind:is="currentTabComponent"></component> </keep-alive> //include表示a,b组件被缓存,其他组件不缓存,exclude代表除了xx组件其他的组件缓存

匹配首先检查组件自身的 name 选项,匿名组件不能被匹配。

<template><div id="app"><button @click="fn">xx</button><keep-alive><components :is="com"></components></keep-alive></div>
</template>
​
<script>
import HelloWorld from './components/HelloWorld.vue'
import HelloWorld2 from './components/HelloWorld2.vue'
export default {name: 'app',data(){return{flag:true,com:"HelloWorld",}},components: {HelloWorld,"HelloWorld2"},methods:{fn(){this.flag=!this.flagthis.com=this.flag?"HelloWorld":"HelloWorld2"}}
}
</script>

10.异步组件(高薪意向)

vue开发过程中,我们会做出特别多特别多的组件,包括login,header,footer,main等等。这样使整个网站看起来就十分的庞大,当我们在打开网页的时候,突然一下子把这些所有的组件加载上来,这么多的请求全部同时开始请求,势必会造成网页打开很慢,使客户得到的是非常差劲的体验。

SPA: single page app

在单页应用中,如果没有用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

异步加载并缓存组件:

1、 异步加载组件:用不到的组件不会加载,因此网页打开速度会很快,当你用到这个组件的时候,才会通过异步请求进行加载;

官方解释:Vue允许将组件定义为一个异步解析(加载)组件定义的工厂函数,即Vue只在实际需要渲染组件时,才会触发调用工厂函数,并且将结果缓存起来,用于将来再次渲染

2、 组件缓存起来:通过异步加载的组件会缓存起来,当你下一次再用到这个组件时,丝毫不会有任何的疑迟,组件很快会从缓存中加载出来。

作业:import和require的区别

方法一:通过webpack2.0的按需加载

//1 全局:Vue.component('component-name',function(resolve){//require 语法告诉 webpack自动将编译后的代码分割成不同的块//这些块将通过 Ajax 请求自动下载require(['./my-async-componnet'],resolve)})//注册全局组件名,但只有一个名字,没有实体,相当于空的
//当需要这个组件时,调用上面的工厂函数,触发webpack的异步加载模块方法
//然后异步请求一个模块,请求成功后,这个模块内容即为组件实体部分,并对应地方渲染,加载内容也缓存下来。//2 局部new Vue({components: {'component-name':function(resolve) {require(['./my-component'], resolve)}}})

方法二:通过webpack2+es2015返回一个promise(主流 )

//1 全局:
Vue.component('component-name',()=> import('./my-async-componnet')//这个 `import` 函数会返回一个 `Promise` 对象,不要问我为什么,人家webpack这样设计的。)
//2 局部:new Vue({components: {'component-name': () =>  import('./my-async-componnet')//这个 `import` 函数会返回一个 `Promise` 对象。}})

方法三:高级异步组件(可以处理加载状态) 常常用在路由中使用,vue 2.3.0+ 新增终极解决方案,要求路由2.4.0+

//工厂对象可以返回一个如下对象,对象里面的一些配置参数
const AsyncComponent = () => ({// 需要加载的组件 (这个 `import` 函数会返回一个 `Promise` 对象。)component: import('./MyComponent.vue'),// 异步组件加载时使用的组件loading: LoadingComponent,// 加载失败时使用的组件error: ErrorComponent,// 展示加载时组件的延时时间。默认值是 200 (毫秒)delay: 200,// 如果提供了超时时间且组件加载也超时了,// 则使用加载失败时使用的组件。默认值是:`Infinity`timeout: 3000
})

示例:

 <template><div id="app"><button @click="fn">xx</button><keep-alive><components :is="com"></components></keep-alive><!-- <components :is="com"></components> --></div></template><script>import HelloWorld from './components/HelloWorld.vue'export default {name: 'app',data() {return {flag: true,com: "HelloWorld",}},components: {HelloWorld,// HelloWorld2,// "HelloWorld2":() =>  import('./components/HelloWorld2.vue')"HelloWorld2": () => ({// 需要加载的组件 (是一个 `Promise` 对象)component: import('./components/HelloWorld2.vue'),// 异步组件加载时使用的组件loading: import('./components/Palcebox.vue'),// 加载失败时使用的组件error: import('./components/Palcebox.vue'),// 展示加载时组件的延时时间。默认值是 200 (毫秒)delay: 200,// 如果提供了超时时间且组件加载也超时了,// 则使用加载失败时使用的组件。默认值是:`Infinity`timeout: 3000})},methods: {fn() {this.flag = !this.flagthis.com = this.flag ? "HelloWorld" : "HelloWorld2"}}}</script><style></style>

组件总结

模板上用的代码: {{}} v-text v-html v-pre v-clock v-bind(:) v-on(@) 事件修饰符 .stop .once .capture .self .native v-xxx directive v-if/v-else v-show v-for v-model $attr $listener 新:v-slot 旧:slot

组件内的代码 data:()=>{} props:[], comments:{}, directives:{}, filters:{}, watch:{}, computed:{}, beforeCreate(){}, created(){}, beforeMount(){}, mounted(){}, beforeUpdate(){}, updated(){}

this可以访问哪些东西? 属性 方法 data $destroy $ emit=>自定义事件

路由

1.相关认识 后端路由:对于前端的网络请求,不同的pathname,去执行后端的不同业务 前端路由:不同的网址对应各自的页面 vue的前端路由:SPA应用要做出路由效果,就得判断当前网址,然后切换组件 vue-router就是专门做切换组件的功能,它是一个单独的技术,依赖vue 就像jQuery和dom操作一样

2.引入 2.1.cdn自己网上随便看看

2.2 自己npm下载引入使用

 //安装到项目中:npm i vue-router --save-dev//注意:这种下载不好,因为打包之后容易出问题npm i vue-router --save或者 npm i vue-router --S在main.js入口文件中引入import Vue from "vue"import VueRouter from "vue-router"//引入路由工具import App from "./App.vue"Vue.use(VueRouter)//注入路由,就是运行路由的相关函数和给vue绑定东西:比如$router//创建路由工具const router=new VueRouter({//routes路由器 生成很多网址对应的切换组件routes:[{path:"/home",component:()=>import("./home.vue")},{path:"/about",component:()=>import("./about.vue")}]})new Vue({router,//把路由挂载到页面render(h){return h(App)}}).$mount("#app")//在App.vue中写 <router-view></router-view>

2.3 cli安装 vue create xxapp 空格键选择router然后回车 自定就会把路由安装好

3.router-view和router-link 标签

router-view:相当于 路由网址匹配到的组件 会渲染到当前组件的这个标签上(学不学的会路由 就看能不能理解这个)

router-link:相当于a标签,给我们提供跳转到某个路由的功能,如果没有匹配到路由就会跳转失败:

<router-link to="/xx"> xx<router-link > 编程式跳转: this.$router.push({path:"路由"})

  1. 路由传参

两种方式:

1、 在router-link 标签的to 属性路由值里添问号”?参数名=参数值“传参,在js 中使用”$route.query.参数名“接收参数值;

2、 在路由规则里定义路由规则”{path:'/路由/:参数名'}“

在router-link 标签的to 属性路由值里添斜杠”/参数值“传参,在js 中使用”$route.params.参数名“接收参数值;

html:       <div id="app" ><h1>Hello App!</h1><p><router-link to="/">首页</router-link><br><router-link to="/list?id=1">新闻部</router-link><br><router-link to="/list?id=2">编辑部</router-link><br><router-link to="/list?id=3">人事部</router-link><br><router-link to="/about/1">关于我们</router-link></p><router-view></router-view></div>
部分js:
路由规则--
const myroutes = [{ path: '/', component: main },{ path: '/list', component: list },{ path: '/about/:mydata', component: about }
]
组件定义内,取参用参--
const list = { template: `<div>列表页面内容{{$route.query.id}}`
}
const about = { template: '<div>关于我们页面{{$route.params.mydata}}</div>' }
​

3、编程式导航

通过js 触发路由切换:

router.push('home')             // 字符串,路由规则中定义的name名
router.push({ path: 'home' })               // 对象
router.push({ name: 'user', params: { userId: 123 }})   // 命名的路由
router.push({ path: '/list', query: { plan: 'private' }})
​
router.replace(location)://跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
​
router.go(n);//参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。
router.go(1)        // 在浏览器记录中前进一步,等同于 history.forward()
router.go(-1)       // 后退一步记录,等同于 history.back()

5.路由嵌套--子路由跳转 *代表所有匹配都通过 redirect代表重定向 //注意:子路由中的匹配地址不要在前面加/ 否则就会认为是根路由

//语法:一个路由对象的设计中中包含: path(路由匹配的路径)  component(匹配路径时对应渲染的组件)  redirect(匹配路径时重新匹配另外的路径)  children(子路由们的数组)   name(路由的名字,方便直接通过名字来匹配路由):
//routes=[{path,component,redirect,children,name},{path,component,redirect,children,name}]
​
const routes = [{path: '/',component: () => import("../views/root.vue"),redirect:"/goods_rank",// 访问/时默认重新访问/goods_rankchildren: [{name:"goods_rank",path: "goods_rank",component: () => import("../views/root/goods_rank.vue"),//goods_rank组件加载的条件是路由匹配上了: localhost:8080/goods_rankchildren: [{name:"goods_rank_all",path: "goods_rank_all",component: () => import("../views/root/goods_rank/goods_rank_all.vue")//goods_rank_all组件加载的条件是路由匹配上了: localhost:8080/goods_rank/goods_rank_all},{path: "goods_rank_highpraise",name:"goods_rank_highpraise",component: () => import("../views/root/goods_rank/goods_rank_highpraise.vue")},{path: "goods_rank_middlepraise",name:"goods_rank_middlepraise",component: () => import("../views/root/goods_rank/goods_rank_middlepraise.vue")},{path: "goods_rank_badepraise",name:"goods_rank_badepraise",component: () => import("../views/root/goods_rank/goods_rank_badepraise.vue")},{path: "*",component: () => import("../views/root/goods_rank/goods_rank_all.vue")}]
​},{path: "goods_inventory",name:"goods_inventory",component: () => import("../views/root/goods_inventory.vue")},{path: "goods_new",name:"goods_new",component: () => import("../views/root/goods_new.vue")},{path: "goods_set",name:"goods_set",component: () => import("../views/root/goods_set.vue")}]
}]

//跳转总结:

注册路由时,路由路径不要在前面加 斜杠

跳转路由时 1.path里写的路径必须前面加斜杠 从根路由路径开始写 2.不用path 直接使用name

6.路由模式--hash&history(面试)

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

const router=new VueRouter({mode:"hash",//"history"routes:[{path:"/home",component:()=>import("./home.vue")},{path:"/about",component:()=>import("./about.vue")}]})

7.VueRouter router route区别 VueRouter是一个nodejs识别的模块包 route是路由匹配时,携带了一些信息的对象,包括path,params,hash,query等等信息 router是路由实例对象,包含了路由的跳转方法,钩子函数等等

8.路由守卫

8.1全局守卫

全局前置钩子router.beforeEach(fn),导航被触发----一般登录验证

全局解析钩子router.beforeResolve(fn),组件初始化 全局后置钩子router.afterEach(fn),没有next,导航被确认,一般路由跳转以后用window把窗口调上去

//全局前置守卫
router.beforeEach((to, from, next) => {//用户未登录只能访问首页、登录注册页面if (to.path == "/" || to.path == "/login" || to.path == "/register") {next();} else {//去其他页面判断是否登录let flag = window.localStorage.getItem("email");//登录过直接放行if (flag) {next()} else {//未登录则跳转到首页Message({message: '您尚未登录哦,请先登录!',type: 'warning',duration: 1500});next("/");}}
})
​
//全局后置守卫
router.afterEach((to, from) => {window.scrollTo(0,0)
})
​
​

8.2路由独享的守卫

路由独享的守卫beforeEnter(to,from,next),路由初始化(组件未初始化)----

a,路由鉴权-----用户体验:界面,功能,bug,效率,权限

b,组件异步加载情景中(插件配置:syntax-dynamic-import)

routes:[{path:"/test",component:()=>import("../components/Test.vue"),beforeEnter(to,from,next){if(to.path==="/test"){alert("请登录");next(false); // 禁止跳转}else{next()}}
}]

3)组件内部生命周期守卫

beforeRouteLeave 从该组件离开

beforeRouteEnter(to,from,next),组件被激活,使用不了this,故构造指定该next 可以接收一个回调函数接收当前vm 实例----路由传参获取参数,得到初始化数据

beforeRouteUpdate(to,from,next),组件重用时被调用----路由传参获取参数,避免增添watch 开销

导航守卫执行顺序:beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < beforeRouteEnter < beforeResolve < afterEach

出发路由,预备从当前组件离开,判断路由变化,判断组件是否重用,判断新路由初始化,判断组件初始化,路由与组件初始化完毕,路由组件重定向完毕

仓库vuex

Vuex简介

1.为了方便实现组件之间的数据共享,Vuex是他们团队借鉴了redux,用来实现vue组件全局状态(数据)管理的一种机制.

2.特点(面试)

能够在vuex中集中管理共享的数据,易于开发和后期维护

能够高效地实现组件之间的数据共享, 提高开发效率

存储在 vuex中的数据都是响应式的,能够实时保持数据与页面的同步

一般情况下,只有组件之间共享的数据,才有必要存储到vuex中;

对于组件中的私有数据,依旧存储在组件自身的data中即可.

3.使用场景

如果你实在不知道开发中什么数据才应该保存到Vuex中,那你就先不用Vuex,需要的时候自然就会想到它

引入方式

1.自己下载引入

2.cli脚手架引入

1.安装:

npm i vuex --save导入:import Vuex from "vuex"Vue.use(Vuex)

创建仓库:

const store=new Vuex.Store({state:{msg:"我就是所有共享的数据"}})

把仓库挂载到vm对象:

new Vue({render(h){return h(app)},router,store//挂载以后  所有的组件就可以直接从store中获取全局数据}).$mount( "#app")

2.vue create app

选择配置vuex

State

1.创建state状态,状态就是那个存数据的对象

const store=new Vuex.store({state:{msg:"我就是所有共享的数据"}})

2.组件中访问数据

this.$store.state.msg

Getter

getter就就像是store的计算属性,它会传入state对象供我们操作 //1.设计

const store = new Vuex.Store({state: {arr: [{ id: 1, text: '...', birth: "1997-02-03" },{ id: 2, text: '...', birth:  "2019-10-03" }]},getters: {bigMans(state) {return state.arr.filter((man)=>{let manY=new Date(man.birth).getFullYear()let nowY=new Date().getFullYear()return (nowY-manY)>=18})}}
})

//2.使用

this.$store.getters.bigMans

Mutation

组件中希望更改 Vuex 的 store 中的状态(数据)的唯一方法是提交 mutation 不要用赋值表达式直接在组件中给store设置新数据 这样设计的原因是,只有通过mutation来更新数据,它才会去帮我们通知所有使用数据的组件更新数据 刷新UI

//1.设计

const store = new Vuex.Store({state: {count: 1},mutations: {//默认传第一个参数传stateincrement (state,obj) {// 变更状态state.count=obj.n}}
})

//2.组件中使用 //2.1 直接触发并传值(提交载荷)

this.$store.commit('increment',{n:100})

//2.2 可以以一个对象的形式传入 //传入对象时,当相于把整个对象作为了第二个参数传入mutations

this.$store.commit({type: 'increment',n:100
})

注意:一条重要的原则就是要记住 mutation 必须是同步处理 下面是错误的方式:

mutations: {increment (state,obj) {//如果fnAsync函数是一个异步业务函数,就会导致更新失败fnAsync(() => {state.count=obj.n})}
}

Action

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

//设计

const store = new Vuex.Store({state: {count: 0},mutations: {increment (state,obj) {state.count=obj.n}},actions: {//默认第一个参数传一个跟store一样的对象increment (context,obj) {//假设fnAsync是一个异步业务函数fnAsync(() => {context.commit('increment',obj)})      }}
})

//使用也是两种: //1.直接分发

this.$store.dispatch('increment',{n:100})

//2.以对象形式分发

this.$store.dispatch({type: 'increment',n:100
})

Module

可用于业务分块开发: 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

//分块设计:
const moduleA = {namespaced: true,//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)   state: { msg:1 },mutations: { change(state,n){state.msg=n} },actions: { change(context,n){context.commit("change",n)} },getters: { x(state){return state.msg} }
}
​
const moduleB = {namespaced: true,//局部命名空间    state: {  msg:1 },mutations: { change(state,n){state.msg=n} },actions: { change(context,n){context.commit("change",n)} },getters: { x(state){return state.msg} }
}
​
const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}
})
​
//组件中的使用:
this.$store.state.a.msg // -> moduleA 的状态
this.$store.state.b.msg // -> moduleB 的状态
this.$store.commit("a/change",100)-> moduleA 的mutations
this.$store.commit("b/change",200)-> moduleB 的mutations
this.$store.getters["a/x"]-> moduleA 的getters
this.$store.getters["b/x"]-> moduleB 的getters
this.$store.dispatch("a/change",100)-> moduleA 的actions
this.$store.dispatch("b/change",200)-> moduleB 的actions

技术厉害的同学拓展:mapState,mapGetters,mapMutation,mapAction和使用常量替代 Mutation 事件类型,

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然

用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。

element等等UI框架

1.安装:

npm i element-ui -S

2.引入

import Vue from 'vue';
import ElementUI
from 'element-ui';//引入组件库
import 'element-ui/lib/theme-chalk/index.css';//全局的css样式
import App from './App.vue';Vue.use(ElementUI);//注册全局组件==>Vue.component("xxxx",{})new Vue({el: '#app',render: h => h(App)
});

Vue3快速上手

1.Vue3简介

  • 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王)

  • 耗时两年多,2600+次提交,30+个RFC,600+次PR,99位贡献者

  • github上的tags地址:Release v3.0.0 One Piece · vuejs/core · GitHub

2.Vue3带来了什么

2.1 性能的提升

  • 打包大小减少41%

  • 初次渲染块55%,更新渲染快133%

  • 内存减少54%

  • ......

    2.2 源码的升级

  • 使用Proxy代替defineProperty实现响应式

  • 重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript

  • vue3.0更好的支持TypeScript

4.新的特性

4.1 Composition API (组合API)

  • setup配置

  • ref和reactive

  • watch和watchEffect

  • provide和inject

4.2 新的内置组件

  • Fragment

  • Teleport

  • Suspence

4.3 其他改变

  • 新的生命周期钩子

  • data选项应该始终被声明为一个函数

  • 移除keyCode支持做为v-on的修饰符

  • .....

Vue3.0环境集成

1.使用vue-cli创建

## 1.查看@vue/cli的版本,确保@vue/cli版本在4.5.0以上
vue --version
## 2.安装或者升级你的@vue-cli
## 3.创建
vue create vueproject
#  4.启动
cd vueproject
npm run serve

2.使用vite创建

官方文档:快速上手 | Vue.js

vite官网:https://vitejs.com

  • 什么是vite? -------- 新一代的前端工具

  • 优势如下:

    • 开发环境中,无需打包,可快速的冷启动

    • 轻量快速的热加载

## 1.创建
npm init vite vapp -- --template vue   生成一个模板页面
npm init vite vapp  可以选择集成的插件(路由 仓库 ts 等等)

常用的Composition API(组合API)

官方文档:组合式 API 常见问答 | Vue.js

1.setup

  • 理解:Vue3.0中一个新的配置向,值为一个函数

  • setup是所有Composition API(组合API) ”表演的舞台“

  • 组件中所有用到的:数据、方法等等,均要配置在setup中

  • setup函数的两种返回值:

    • 若返回一个对象,则对象中的数据、方法、在模板中均可直接使用(重点)

      <template><div class="home"><h1>显示vue3.0的数据和方法</h1><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><button @click="sayName()">点击测试vue3.0的方法</button></div>
      </template>
      ​
      <script>
      ​
      export default {setup() {// 现在暂时还不考虑响应式数据的问题// 数据 let name = "李国栋";let age = 18;
      ​// 方法let sayName = function () {console.log(`我的名字是${name},我的年龄是${age}`);}
      ​return {name, age, sayName}}
      ​
      }
      </script>
      ​
    • 若返回一个渲染函数,则可以自定义渲染内容(了解)

      //页面中会显示一个h1标签,标签的内容是"vue3返回渲染函数"
      <script>
      import {h} from 'vue'
      export default {setup() {// 返回一个渲染函数// h相当于document.CreateElement()return ()=>{return h("h1","vue3返回渲染函数")}}
      }
      </script>
      ​
  • 注意点

    • 尽量不要和Vue2.x配置混用

    • Vue2.x配置(data,methods,computed......)中可以访问setup中的属性,方法

    • 但在setup中不能访问到Vue2.x配置(data,methods,computed......)

    • 如有重名,setup优先

    • setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性

2.ref函数

  • 作用:定义一个响应式的数据

  • 语法:const xxx = ref("value")

    • 创建一个包含响应式的数据的引用对象(reference对象)

    • js中操作数据:xxx.value

    • 模板中读取数据不需要.value,直接<div>{{xxx}}</div>

  • 注意

    • 接收的数据类型可以是基本数据类型也可以是引用数据类型

    • 基本类型的数据:响应式依然是靠Object.defineProperty()的get和set完成的

    • 对象类型的数据:内部“求助”了Vue3.0的一个新的函数------reactive函数

<template><div class="home"><h1>显示vue3.0的数据和方法</h1><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>工作:{{more.job}}</h2><h2>薪资:{{more.salary}}</h2><button @click="changeMsg()">点击测试vue3.0的基本数据</button><button @click="changeMore()">点击测试vue3.0的引用数据</button></div>
</template>
​
<script>
import { ref } from "vue"
export default {setup() {// 1.基本数据类型let name = ref("李国栋");let age = ref(18);let more = ref({job: "前端开发工程师",salary: "10k"})
​// 方法let changeMsg = function () {name.value = "余梦佳";age.value = 19;console.log(name, age);}
​let changeMore = function () {more.value.salary = "20K";console.log(more.value);}
​return {name, age, changeMsg, more, changeMore}}
​
}
</script>
​

3.reactive函数

  • 作用:定义一个对象类型的响应式数据(基本数据类型别用它,用ref函数)

  • 语法:const 代理一个对象 = reactive(被代理的对象) 接收一个对象(或数组),返回一个代理器对象(proxy对象)

  • reactive定义的响应式数据是“深层次的”

  • 内部基于ES6的Proxy实现,通过代理对象内部的数据都是响应式的

<template><div class="home"><h1>显示vue3.0的数据和方法</h1><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>工作:{{more.job}}</h2><h2>薪资:{{more.salary}}</h2><h2>c:{{more.a.b.c}}</h2><button @click="changeMsg()">点击测试vue3.0的基本数据</button><button @click="changeMore()">点击测试vue3.0的引用数据</button></div>
</template>
​
<script>
//1.引入
import {ref,reactive} from "vue"
export default {setup() {let name = ref("李国栋");let age = ref(18);let more = reactive({job: "前端开发工程师",salary: "10k",a:{b:{c:"ccccccccccccc"}}})
​// 方法let changeMsg = function () {name.value = "余梦佳";age.value = 19;console.log(name, age);}
​//2.修改 let changeMore = function () {more.salary = "20K";more.a.b.c = "55555555555555555555"}
​return {name, age, changeMsg, more, changeMore}}
​
}
</script>

4.Vue3.0中的响应式原理

vue2.0的响应式

  • 实现原理

    • 对象类型:通过Object.definedProperty()对属性的读取、修改进行拦截(数据劫持)

    • 数组类型:通过重写更新数据的一系列方法来实现拦截。(对数组的方法进行了包裹)

      Object.defineProperty(data,"count",{get(){},set(){}
      })

    存在问题:

    • 新增属性,删除属性都不会刷新界面

    • 直接通过下标修改数组,界面不会自动更新

            let person = {name:"李国栋",age:18}
    ​let p = {};
    ​Object.defineProperty(p,"name",{get(){console.log("有人读取数据时调用");return person.name},
    ​set(value){console.log("有人修改了数据,我要去更新页面");person.name = value}})

vue3.0的响应式

  • 实现原理

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

    • 通过Reflect(反射):对被代理对象的属性进行操作

    • MDN文档中描述的Proxy与Reflect:

      • Proxy:Proxy - JavaScript | MDN

      • Reflect:Reflect - JavaScript | MDN

        let person = {name:"李国栋",age:18}
​let p = new Proxy(person,{// 读取get(target,proname){// target表示原对象  proname表示对象名console.log("有人读取了person上的属性",target);return target[proname]},// 修改或者增加set(target,proname,value){console.log("有人修改了person上的属性,我要去更新了");target[proname] = value},// 删除deleteProperty(target,proname){console.log("有人删除了person上面的属性,我要去调用了");return delete target[proname]},});

5.计算属性

  • 与vue2.x中computed配置功能一致

  • 写法

<template><div class="home"><h1>vue2.0的计算属性--------{{total1}}</h1>
​<div v-for="(el,index) in arr2" :key="index"><span>{{el.title}}---</span><span>{{el.price}}--</span><button @click="jianshao(index)">-</button><span>{{el.count}}</span><button>+</button></div>
​<h1>vue3.0的计算属性----------{{total2}}</h1></div>
</template>
​
<script>
​
// 注意:vue2.x和vue3.x不要混用
// 1.引入
import { reactive, computed } from "vue"
export default {
​data() {return {arr: [{title: "鱼香肉丝",price: 10,count: 1},{title: "麻婆豆腐",price: 10,count: 2},{title: "米饭",price: 1,count: 3}],};},computed: {total1() {return this.arr.reduce((a, b) => {return a + b.price * b.count}, 0)}},
​setup() {
​let arr2 = reactive([{ title: "鱼香肉丝", price: 18, count: 1 },{ title: "鱼香肉丝2", price: 20, count: 2 },{ title: "鱼香肉丝3", price: 30, count: 4 }])
​// 2.使用let total2 = computed(() => {return arr2.reduce((n1, n2) => (n1 + n2.price * n2.count), 0)})
​// 减少let jianshao = function(index){arr2[index].count--}return { arr2, total2,jianshao }
​}
​
}
</script>
​

6.监听属性

  • 与vue2.x中的watch配置功能一致

  • 注意

    • 监视reactive定义的响应式数据时,oldvalue无法正确获取,强制开始了深度监视(deep的配置失效)

    • 监视reactive定义的响应式数据的某一个值时:deep配置有效

​
<template><div class="home"><h1>{{num}}</h1><h1>{{msg}}</h1>
​<button @click="num++">点击num++</button><button @click="addmsg()">点击msg+!</button></div>
</template>
​
<script>
​
// 注意:vue2.x和vue3.x不要混用
// 1.引入
import { watch, ref,reactive } from "vue"
export default {
​data() {return {
​}},computed: {},
​
​setup() {
​let num = ref(0);let msg = ref("hello");let person = reactive({name:"李国栋",age:18,more:{job:"前端开发工程师",salary:"12k"}})
​// 1.监听ref定义的响应式数据watch(num, (newvalue, oldvalue) => {console.log("num变量", newvalue, oldvalue);})
​
​// 2.监听多个ref定义的响应式数据watch([num, msg], (newvalue, oldvalue) => {console.log("msg数据变化了", newvalue, oldvalue);})
​let addmsg = () => {msg.value += "!"}//3.监听reactive所定义的一个响应式数据的全部属性//注意:此处无法正确的获取oldvalue//注意:并且强制开启了深度监视(deep配置无效)watch(person, (newvalue, oldvalue) => {console.log("person数据变化了", newvalue, oldvalue);},{deep:false})//4.监听reactive所定义的一个响应式数据的某一个属性watch(()=>person.name, (newvalue, oldvalue) => {console.log("person数据变化了", newvalue, oldvalue);},{deep:false})
​return { num, msg, addmsg }
​}
​
}
</script>
​
​

7.Vue3生命周期

  • 什么是生命周期

    Vue中每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁、这就是一个组件所谓的生命中周期

  • Vue2.x中的生命周期

    beforeCreate   created
    beforeMount    mounted
    beforeUpdate  updated
    beforeDestroy  destroyed
    activated
    deactivated
    errorCaptured 
  • Vue3.x的生命周期

    在Vue3.x中,新增了一个setup生命周期函数,setup执行的时机是在beforeCreate生命函数之前执行,因为在这个函数中不能通过this来获取实例的;同时为了命名的统一,将beforeDestory改名为beforeUnmount,destoryed改名为unmounted

    beforeCreate(建议使用setup代替)created(建议使用setup代替)
    setup
    beforeMount     mounted
    beforeUpdate    updated
    beforeUnmount   unmounted

    vue3新增了生命周期钩子,我们可以通过在生命周期函数前加on来访问组件的生命周期

    Composition API 形式的生命周期钩子

    onBeforeMount  onMounted
    onBeforeUpdate  onUpdated
    onBeforeUnmount  onUnmounted
    onErrorCaptured
    onRenderTracked
    onRenderTriggered
    <script>
    import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref
    } from 'vue'export default {setup () {// 其他的生命周期onBeforeMount (() => {console.log("App ===> 相当于 vue2.x 中 beforeMount")})onMounted (() => {console.log("App ===> 相当于 vue2.x 中 mounted")})// 注意,onBeforeUpdate 和 onUpdated 里面不要修改值onBeforeUpdate (() => {console.log("App ===> 相当于 vue2.x 中 beforeUpdate")})onUpdated (() => {console.log("App ===> 相当于 vue2.x 中 updated")})onBeforeUnmount (() => {console.log("App ===> 相当于 vue2.x 中 beforeDestroy")})onUnmounted (() => {console.log("App ===> 相当于 vue2.x 中 destroyed")})return {}}
    }
    </script>
    ​

8.Teleport

Vue 鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。

然而,有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置

模态弹框

to属性:放到指定位置

<template><div class="reson"><button @click="showModel">点击弹出模态框</button>
​<teleport to="body"><div class="mask" v-show="isShow"><div class="box"><h1>我是模态框</h1><button @click="closeModel">点击关闭模态框</button></div></div></teleport>
​</div>
</template>
​
<script>
import { ref } from "vue"
export default {name: 'Reson',
​setup() {let isShow = ref(false)//显示let showModel = function () {isShow.value = true}//隐藏let closeModel = function () {isShow.value = false}return { isShow, showModel, closeModel }}
​
};
</script>
​
<style lang="scss" scoped>
.reson {background-color: pink;
}
.mask {width: 100%;height: 100%;position: absolute;top: 0;left: 0;background-color: rgba(0, 0, 0, 0.5);
}
.box {width: 300px;height: 300px;background-color: aqua;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
}
</style>

9.属性

  1. 组件内部 还是可以用2.0 做项目时愿意用2.0的同学语法不变

<script>
export default {pops:["msg"],// pops:{msg:String}, setup(){}
}
< /script>
<template><div>{{msg}}</div>
</template>​父组件:<Box :msg="n"></Box>​

2.vue3组件内部组合式API setup中取属性值

<script>
export default {pops:["msg","count"],// pops:{msg:String}, setup(props){let fn=()=>{console.log(props.msg,props.count)}//必须在组件中注册属性不然setup函数收不到数据return {fn}}
}
< /script>
<template><div><p> {{msg}}</p><button @click="fn">look</button></div>
</template>父组件:<Box :msg="n" :count="200"></Box>

3.setup语法糖中获取属性值

<script setup>import  {defineProps} from "./Box1.vue"//注意3.2之后不用引入可以直接使用let obj=defineProps(["msg","count"])let fn=()=>{console.log(obj.msg,obj.count)}
}
< /script>
<template><div><p> {{obj.msg}}</p><button @click="fn">look</button></div>
</template>​父组件:<Box :msg="n" :count="200"></Box>

10.自定义事件

  • 事件名

    与组件和 prop 一样,事件名提供了自动的大小写转换。如果在子组件中触发一个以 camelCase (驼峰式命名) 命名的事件,你将可以在父组件中添加一个 kebab-case (短横线分隔命名) 的监听器。

//父组件HomeView
<template><div><MyBox1 @myclick="fn" @my-event="fm"></MyBox1></div>
​
</template>
​
<script setup>
import MyBox1 from "../components/MyBox1.vue"
​
let fn = (arg) => {console.log(6666, arg)
}
​
let fm = (arg) => {console.log(77777, arg);
}
​
</script>
​
//子组件MyBox1
<template>
​<div @mousemove="change1"><button @click="change">点击触发myevent事件</button></div>
​
</template>
​
<script >
​
export default {
​methods: {change() {this.$emit('myclick', "helo666");},change1() {this.$emit('myEvent', "121212");
​}},
​
}
</script>
  • 定义自定义事件

可以通过 emits 选项在组件上定义发出的事件

//父组件
<HomeView @my-event="fn" @my-submit="fn1"></HomeView>
​
//子组件
<template><div @mouseenter="fm"><button @click="fn">点我触发mybtn组件内部的myclick事件</button>        </div>
</template>
​
<script>export default {emits:["myEvent","myclick"],methods:{fn(){this.$emit("myClick",200)},fm(){this.$emit("click",300)}}}
</script>
  • v-model 参数

//父组件
<HomeView v-model:title="bookTitle"></HomeView>
​
//子组件
//1.接收参数
props:["title"]
//2.定义事件
emits: ['update:title']
//3.触发事件
this.$emit("update:title","子组件传递给父组件的值")
  • 多个 v-model 绑定

//2.0这样写会有矛盾
<MyVmodel v-model="msg" v-model="msg2" v-model="msg3"></MyVmodel> //2.0应该这样写:
<MyVmodel v-model="msg" :msg2="msg2" @changemsg2="changemsg2" :msg3="msg3" @changemsg3="changemsg3">
</MyVmodel>
js:
changemsg2(arg){this.msg2=arg}
changemsg3(arg){this.msg3=arg}//3.0
<MyVmodel v-model="msg" v-model:msg2="msg2" v-model:msg3="msg3"></MyVmodel>
​
//父组件
<user-namev-model:msg1="msg1"v-model:msg2="msg2"
></user-name>
​
//子组件
//1.接收参数
props:["msg1","msg2"]
//2.定义事件
emits: ['update:msg1','update:msg2']
//3.触发事件
this.$emit('update:msg1',"子组件传递给父组件的值1")
this.$emit('update:msg2',"子组件传递给父组件的值2")

11.状态驱动的动态 CSS

单文件组件的 <style> 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上

<template><div class="box1">hello</div><button @click="changcolor">修改box1的样式</button><div class="box2">hello66666</div><button @click="changother">修改box2的样式</button>
</template>
​
<script setup>
​
import { ref, reactive } from "vue"
​
​
let color = ref("red")
let changcolor = () => {color.value = "blue"
}
​
let other = reactive({width: "200px",height: "100px"
})
​
let changother = () => {other.width = "400px"
}
​
</script>
​
<style lang="scss" scoped>
.box1 {color: v-bind('color');
}
​
.box2 {background-color: yellow;width: v-bind('other.width');height: v-bind('other.height');
}
</style>

12.注册组件

  1. 组件内部

​
<script>import Box1 from "./Box1.vue"export defult{components:{Box1},setup(){}}
</script>
<template><Box1></Box1>
< /template>
​

2.vue3组件内部组合式API setup语法糖中注册组件

<script setup>import Box1 from "./Box1.vue"  //只需要导入 不用写注册代码  会在打包的时候自动帮我们注册
</script>
<template><Box1></Box1>
< /template>

3.注册全局组件

//main.js文件:
import { createApp} from 'vue'
import App from './App.vue'
const app=createApp(App)
​
import Box1 from "./Box5.vue"
app.component(Box1.name,Box1)
​
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了
​
//App.vue文件:
<template><Box1></Box1>
< /template>

4.定义同步组件:

//Box1.vue文件:
<script>import {defineComponent} from "vue"export default defineComponent({data(){return {}},methods:{},     setup(){}       });
</script>

5.1定义局部异步组件:

组件内部

<script>import {defineAsyncComponent} from "vue"let Box1 = defineAsyncComponent(() => import("./Box1.vue")) //注意3.2之后不用引入defineAsyncComponentexport default {components: {Box1},setup() {}}
</script>

setup语法糖:

<script setup>import  Box1 from "./Box1.vue"import  Box2 from "./Box2.vue"import {defineAsyncComponent} from "vue"let Box3=defineAsyncComponent(()=>import("./Box3.vue"))//注意3.2之后不用引入defineAsyncComponent,而且这个变量名直接就是注册的组件名(打包时自动注册的)
</script>

5.2定义全局异步组件:

//main.js文件:
import { createApp,defineAsyncComponent} from 'vue'
import App from './App.vue'
const app=createApp(App)
let Box1=defineAsyncComponent(()=>import("./Box4.vue"))
app.component("Box1",Box1)
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了

13.Suspense

  • 等待异步组件时渲染一些额外的内容,让应用有更好的用户体验

    <suspense> 组件有两个插槽。它们都只接收一个直接子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。

    <template><div class="fa"><h1>父组件HomeView</h1>
    ​<!-- <Helloworld></Helloworld> --><suspense><template #default><MyChild></MyChild></template><template #fallback><div><h1> 正在加载中Loading...</h1></div></template></suspense></div>
    </template>
    ​
    <script setup>
    import { defineAsyncComponent } from "vue"
    // import Helloworld from "../components/HelloWorld.vue"  //静态引用
    ​
    let MyChild = defineAsyncComponent(() => import("../components/HelloWorld.vue")) //异步引入
    ​
    </script>
    ​
    <style lang="scss" scoped>
    .fa {height: 300px;background-color: #ccc;
    }
    </style>

14. isRef toRef toRefs readonly

isRef 判断是否为响应性变量

<script setup>
import {ref,isRef} from "vue"
let msg=ref(0)
function fn(){console.log(isRef(msg)) //判断是否为响应性变量  msg.value++
}< /script>

toRef 把引用数据的属性值设置为变量 并且关联和设置为响应性变量

import {ref,isRef,reactive,toRef,toRefs} from "vue"
let obj=reactive({x:20,y:20})
// obj.x
// obj.y
​
// let {x,y}=obj//let x=obj.x,y=obj.y
// function fm(){
//   x=10000
// }
// function look(){
//  console.log(x)
// }
​
let x=toRef(obj,"x")// 隐式 let x=ref(obj.x);并且响应到obj对象去
function fm(){x.value=10000
}
function look(){console.log(x,obj.x)//10000 10000
}

toRefs 引用数据响应式解构

import {isRef,reactive,toRef,toRefs} from "vue"
//1.引用数据普通解构
let obj1=reactive({x:100,y:200})
let {x,y}=obj1//x,y变量不具备响应功能
​
​
//2.引用数据响应式解构
let obj2=reactive({a:10,b:20})
let {a,b}=toRefs(obj2)//a,b变量具备响应功能
function  change2 () {a.value++b.value++
}
function  look2 () {console.log(a.value,obj2.a,isRef(a))// 11 11 true
}
</script>

readonly 把数据变为只读功能

import {isRef,reactive,toRef,toRefs,readonly} from "vue"
let obj=readonly({}) 

公共数据配置

因为v2使用公共配置时一般绑定在原型上无论是否使用都在每一个组件的this链上,这样的设计不太友好,v3提供了专门公共数据配置的方式: globalProperties getCurrentInstance

//main.js
const app=createApp(App)
​
app.config.globalProperties.$hqyj=公共数据
​
//组件中钩子中:
//getCurrentInstance()获取当前组件实例对象
//当前组件的实例对象解构的proxy对象中就有$hqyj
//注意点是这个函数要在组件钩子中使用,不要在普通函数中使用
​
<script setup>import {onBeforeMount,getCurrentInstance,effect} from "vue"const { proxy } = getCurrentInstance();//正常使用:语法糖环境默认为setup钩子onBeforeMount(()=>{console.log(getCurrentInstance())//正常使用:标准钩子})effect(()=>{console.log(getCurrentInstance())//正常使用:副作用钩子})
​let fn=()=>{console.log(getCurrentInstance())//错误使用:获取null,自定义函数}
</script>

网络配置

代理配置:

//vite环境:
export default defineConfig({plugins: [vue()],server: {// port:"8080",// hostproxy: {'/api': {target: 'http://127.0.0.1:7001', // 代理的目标地址rewrite: (path) => path.replace(/^\/api/, '/'), // 路径重写changeOrigin: true,// secure: true, // target是否https接口// ws: true, // target是否代理websockets               }}}
})​
//webpack环境不变:
module.exports={lintOnSave:false,devServer:{port:"10086",host:"localhost",proxy:{"/":{target:"http://127.0.0.1:7001",changeOrigin:true,pathRewrite:{"^/":""}}}}
}

网络配置:

//mian.js
import axios from "axios"
const app=createApp(App)
axios.defaults.baseURL="http://127.0.0.1:5173/api"
app.config.globalProperties.$axios=axios
app.config.globalProperties.hqyj=666
app.mount('#app')
​
//组件.vue
<script setup>import {ref,reactive,computed,onBeforeMount,getCurrentInstance,effect} from "vue"   const { proxy } = getCurrentInstance();//注意在组件钩子中获取,不要在事件中let fn=()=>{proxy.$axios("/test")}
​

app.use

插件配置

同2.0一样use函数接受一个函数或者对象(对象有install函数) 然后会调用这个传入的回调函数 给它传参app对象,以此来实现第三方插件

//main.js文件
import { createApp} from 'vue'
import App from './App.vue'
const app=createApp(App)
​
​
import $hqyj from "./http/$hqyj.js"
app.use($hqyj)
​
​
import $axios from "./http/$axios.js"
app.use($axios)
​
​
app.mount('#app')​
//$hqyj.js文件
function $hqyj(app){app.config.globalProperties.$hqyj="5000e"
}
export default $hqyj;
​
​
//$axios文件
import axios from "axios"
function $axios(app){axios.defaults.baseURL="http://127.0.0.1:5173/api"app.config.globalProperties.$axios=axios
}
export default $axios;
​
​
//组件中就可通过获取实例对象 来获取公共属性中的$hqyj的"5000e"  $axios的网络工具
//组件内:
//1.钩子函数中获取实例对象:
//2.获取公共属性中的数据
<script setup>import {onMounted,getCurrentInstance} from "vue"// import axios from "axios"let {proxy}=getCurrentInstance()onMounted(async ()=>{//axios.defaults.baseURL="http://127.0.0.1:5173/api"        let res=await proxy.$axios('/test')//"http://127.0.0.1:5173/api/test"==>"http://127.0.0.1:7001/test"console.log(res,proxy.$hqyj,11111111111)})
​

路由配置

使用思路跟之前差不多 语法略微有变化

npm i vue-router

//路由文件
import { createRouter, createWebHistory } from 'vue-router'
const routes = [{path: '/',name: 'home',component: () => import('../views/Home.vue'),//独享守卫beforeEnter(to,from,next){next()}},{path: '/car',name: 'car',component: () => import('../views/Car.vue'),children:[{path: '/car/son1',name: 'son1',component: ()=>import("../views/Son1.vue")  }]}
]
const router = createRouter({history: createWebHistory(),routes
})
//全局守卫
router.beforeEach((to,from,next)=>{console.log()next()
})
​
router.beforeResolve((to,from,next)=>{next()
})
​
router.afterEach((to,from)=>{})
​
export default router
​
​
//main.js文件
import router from "./router/index.js"
app.use(router)//记得在mount之前调用
​
​
​
//组件内部的使用
import {onBeforeRouteLeave,useRouter,useRoute} from "vue-router"let router=useRouter()
​let fn=()=>{let route=useRoute()    console.log(route.query)router.push({path:"/car/son1",query:{id:123}})}//组件内的守卫钩子onBeforeRouteLeave((to,from,next)=>{console.log(66666,"离开")next()})
​
router-view和router-link组件不变

数据仓库配置

vuex

vuex3.0适用于vue2.0

vuex4.0适用于vue3.0和2.0

Pinia 适用于3.0 为了兼容2.0也能使用 主要是给3.0的组合式API设计的

官方中文网: Pinia 中文文档

1.安装

npm install pinia

2.写一个store文件

//  store/index.js文件
import { defineStore } from 'pinia'
//这里官网是单独导出  是可以写成默认导出的  官方的解释为大家一起约定仓库用use打头的单词 固定统一小仓库的名字不易混乱
const useStore = defineStore('storeId', {// 这里易错语法:如果箭头函数直接指向返回值的同学  记得把返回的对象用小括号括起来state: () => {return {msg: 'hello'}}
})
​
//main.js文件
import { createPinia } from 'pinia'
let app=createApp(App)
app.use(createPinia())
​

3.组件中的使用

<script setup>import useStore from "../store/index.js"let store=useStore()console.log(store,msg,111)
</script>
​

4.修改数据

//修改仓库状态1:
<script setup>import useStore from "../store/index.js"let store=useStore()let fm=()=>{store.msg="666修改成功"}//store是一个响应性对象 因此可以修改并刷新let {msg}=storelet fn=()=>{ msg="6666修改失败"} //解构之后修改是不行的,因为解构后msg为非响应式变量 同toRefs知识点//解决方案:利用toRefs把响应式对象的属性解构为响应性的变量import {toRefs} from "vue"import useStore from "../store/index.js"let store=useStore()let {msg}=toRefs(store)let fn=()=>{msg.value="6666修改成功啦"} //一定不要忘了value
</script>
​
//修改仓库状态2:
store.$reset()//将状态 重置 到其初始值
​
​
//修改仓库状态3:
store.$patch({msg:"666修改成功",count:store.count+100,
})
store.$patch((state)=>{state.msg="666修改成功";state.count= state.count+100,
})
//问:$patch整体修改个单条修改的区别?
store.msg="666修改成功"
store.count=store.count+100
//答:状态刷新一次,可以批量修改

5.订阅修改

//可以通过 store 的 $subscribe() 方法查看状态及其变化,通过patch修改状态时就会触发一次
store.$subscribe((mutation, state) => { // 每当它发生变化时,将整个状态持久化到本地存储localStorage.setItem('test', JSON.stringify(state))
})

6.Getter

Getter 完全等同于 Store 状态的 计算值。 它们可以用 defineStore() 中的 getters 属性定义。 他们接收“状态”作为第一个参数以鼓励箭头函数的使用:(ps:虽然鼓励但是依然提供了非es6玩家的使用方式 内部可以用this代表state)

//store/index.js文件
export const useStore = defineStore('main', {state: () => ({counter: 0,}),getters: {doubleCount: (state) => state.counter * 2,},
})
​
//组件中直接使用:  <p>Double count is {{ store.doubleCount }}</p>

7.Actions

在pinia中没有提供mutaion 因为Actions就够了(它可以异步同步的统一修改状态)

之所以提供这个功能 就是为了项目中的公共修改状态的业务统一

export const useStore = defineStore('main', {state: () => ({counter: 0,}),actions: {increment() {this.counter++//1.有this},randomizeCounter(state) {//2.有参数   想用哪个用哪个state.counter = Math.round(100 * Math.random())},randomizeCounter(state) {//异步函数axios("/test").then(res=>{state.counter = res.data.length})}},
})
​
//组件中的使用:setup() {const store = useStore()store.increment()store.randomizeCounter()}

8.模块化

一个文件一个小仓库,仓库之间可以相互访问数据 不同组件也可以访问任意小仓库数据

//store/car.js文件
export const car = defineStore('car', {state: () => ({counter: 0,}),actions: {}
})
​
//store/userinfo.js文件
export const userinfo = defineStore('userinfo', {state: () => ({counter: 0,}),actions: {fn(state){state.counter=100}}
})
​
​
​
//A组件可以用car,userinfo两个仓库的数据
import {car} from "@/store/car.js"
import {userinfo} from "@/store/userinfo.js"
let store1=car()
store1.counter
let store2=userinfo()
store2.counter
​
//car仓库使用userinfo仓库跟组件一样的使用方式
import {userinfo} from "@/store/userinfo.js"
let store2=userinfo()
store2.counter
store2.fn()

vue2-3版本笔记相关推荐

  1. vue2.x版本+element-ui2.15+版本实现只能输入数字的ip输入框,功能样式借鉴windows,与父组件双向绑定

    目录 1.双向绑定 2.:oninput动态绑定和@input事件处理 3.主要功能 4.自定义组件代码 1.双向绑定 实现双向绑定在vue2.x版本中是使用v-model实现的,本文子类中的主要代码 ...

  2. 安装Vue2.6版本

    卸载原来版本: npm install -g vue-cli 安装vue2.6版本: npm install -g vue-cli@2.6

  3. Vue2.0学习笔记一 :各种表达式

    #,过滤器 #,在Vue2.x中,过滤器只能在mustache绑定中使用,为了在指令帮定中实现同样的行为,你应该使用计算属性:     #,过滤器可以串联 {{ message | filterA | ...

  4. vue2.x学习笔记

    Vue.config.productionTip=false //阳止 vue在启动时生成生产提示. BootCDN MOMENT.JS 日期处理库 dayjs 解构赋值连续写法 {query:{id ...

  5. vue2.x 学习笔记for

    文章目录 Vue指令之`v-for`和`key`属性 v-for循环普通数组 v-for循环对象数组 v-for循环对象 v-for迭代数字 v-for循环中key属性的使用 Vue指令之v-for和 ...

  6. vs 更换cuda版本笔记

    换了一台电脑,vs编译项目时,需要更换cuda版本, vs直接打开项目会报错,解决方法测试成功. 更换cuda方法,需要三个步骤: 在project_xxx.vcxproj中更换cuda版本 1.比如 ...

  7. vue2.0读书笔记2-进阶

    一.深入响应式原理 二.过渡效果 三.过渡状态 四.Render函数 五.自定义指令 六.混合 七.插件 八.单文件组件 九.生产环境 十.路由 vue-router: http://router.v ...

  8. Tensorflow2.0版本 笔记

    文章目录 Tensorflow笔记 1 常用函数 1.1 tf.where() 1.2 np.mgrid() 1.3 tf.nn.softmax_cross_entropy_with_logits() ...

  9. vue2.0版本指令v-if与v-show的区别

    v-if: 判断是否加载,可以减轻服务器的压力,在需要时加载. v-show:调整css dispaly属性,可以使客户端操作更加流畅. v-if示例: <!DOCTYPE html> & ...

最新文章

  1. 对PostgreSQL中 index only scan 的初步理解
  2. DBCC CHECKDB 数据库或表修复
  3. python中import random_python import random
  4. CentOS 6.3安装MPlayer
  5. 软件测试的流程结构图
  6. springboot-shiro-cas-redis集成session共享,权限共享
  7. ArcBlock 问答 | 扎实打造影响整个区块链生态的产品
  8. js html监听ctrl v,js监听组合按键
  9. 今天是我的生日,也是我的离职日!
  10. call和apply详解-利用借充电宝模拟用法
  11. Java核心技术第一周学习总结
  12. 微信撤回服务器保存吗,收藏!微信消息被撤回?闪照无法保存?这样一步完美解决...
  13. 最新大数据可视化展示效果,别误会了,这才是可视化该有的样子
  14. Selenium基础 — 多窗口操作
  15. 适用于渗透测试不同阶段的工具收集整理
  16. Jmeter做压力测试
  17. 猫耳FM音频转换成MP3格式
  18. 打印服务Print Spooler开启后自动关闭
  19. linux dns劫持转发,linux的dns被劫持(解决方案)
  20. java邮件开发--电子邮件基础

热门文章

  1. ips细胞最新发现:科学家开发出了一套评估干细胞的全能性的新标准
  2. git@gitlab.com: Permission denied报错
  3. 2018提高组模拟13
  4. 利用Py-Socket模块做的一个不登陆windows服务器自动实现替换或者调用自动拨号功能...
  5. 超级壁纸android,超级壁纸大全app下载-超级壁纸大全 安卓版v1.1.0-PC6安卓网
  6. bzoj1671 Knights of Ni 骑士 BFS
  7. 自定义Android桌面小部件
  8. 国企的判断标准是什么 怎么查企业是私企还是国企
  9. word中图片为嵌入式格式时显示不全_图片在word中显示不全怎么处理_word图片显示不全怎么办-win7之家...
  10. vue-admin-elem对接地图报错Unable to preventDefault inside passive event listener invocation.