赞
踩
- var声明变量可以重复声明,而let不可以重复声明
- var是不受限于块级的,而let是受限于块级
- var会与window相映射(会挂一个属性),而let不与window相映射
- var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
- const声明之后必须赋值,否则会报错
- const定义不可变的量,改变了就会报错
- const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
- (1)用了箭头函数,this就不是指向window,而是父级(指向是可变的)
- (2)不能够使用arguments对象
- (3)不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
- (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数
- 应用场景Set用于数据重组,Map用于数据储存Set:
- (1)成员不能重复
- (2)只有键值没有键名,类似数组
- (3)可以遍历,方法有add, delete,has
- Map:
- (1)本质上是健值对的集合,类似集合
- (2)可以遍历,可以跟各种数据格式转换
- ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法
- //定义类
- class Point {
- constructor(x,y) {
- //构造方法
- this.x = x; //this关键字代表实例对象
- this.y = y;
- } toString() {
- return '(' + this.x + ',' + this.y + ')';
- }
- }
- 两者都是外部引入CSS的方式,那么二者有什么区别呢?
-
- @import是CSS提供的语法规则,只有导入样式表的作用;link是HTML提供的标签,不仅可以加载CSS文件,还可以定义RSS,rel连接属性等;
- 加载页面时,link引入的CSS被同时加载,@import引入的CSS将在页面加载完毕后加载;
- link标签作为HTML元素,不存在兼容性问题,而@import是CSS2.1才有的语法,故老版本浏览器(IE5之前)不能识别;
- 可以通过JS操作DOM,来插入link标签改变样式;由于DOM方法是基于文档的,无法使用@import方式插入样式;
- 建议link的方式引入CSS
- //indexof 去重
- var arr =[1,-5,-4,0,-4,7,7,3];
- function unique(arr){
- var arr1 = []; // 新建一个数组来存放arr中的值
- for(var i=0,len=arr.length;i<len;i++){
- if(arr1.indexOf(arr[i]) === -1){
- arr1.push(arr[i]);
- }
- }
- return arr1;
- }
- console.log(unique(arr)); // 1, -5, -4, 0, 7, 3
-
- //set 去重
- let arr = [12,43,23,43,68,12];
- let item = [...new Set(arr)];
- console.log(item);//[12, 43, 23, 68]
- let str = 'hello world!';
- console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
- console.log(str.split(' '))// ["hello", "world!"]
- let ss = [1,2,3,4,4,5,6,7,7,8,9];
- let sss = [...new Set(ss)];
- let data=0;
- for (i of sss) {
- data += i ;
- }
- console.log(data)//45
- forEach更多的用来遍历数组
- for in 一般常用来遍历对象或json
- for of数组对象都可以遍历,遍历对象需要通过和Object.keys()
- for in循环出的是key,for of循环出的是value
- import {hello} from "./hello.js"// 只导入一个
- import {hello,hellos,helloss} from "./hello.js"// 导入多个
- import * as example from "./hello.js"// 导入一整个模块
- export var hello= 'zan';//可以将export放在任何变量,函数或类声明的前面
- export var hellos= 'lisi';
- //也可以使用大括号指定所要输出的一组变量
- var firstName = 'zan';
- var lastName = 'lisi';
- export {firstName, lastName, year};
- //使用export default时,对应的import语句不需要使用大括号
- let bosh = function crs(){}
- export default bosh;
- import crc from 'crc';
- //不使用export default时,对应的import语句需要使用大括号
- let bosh = function crs(){}
- export bosh;
- import {crc} from 'crc';
对于短时间内连续触发的事件(例如滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。
- <div id="" style="width: 100%;height: 20000px;">
-
- </div>
- <script>
- function debounce(fn,delay){
- let timer = null //借助闭包
- return function() {
- if(timer){
- clearTimeout(timer)
- }
- timer = setTimeout(fn,delay) // 简化写法
- }
- }
- // 然后是旧代码
- function showTop () {
- var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
- console.log('滚动条位置:' + scrollTop);
- console.log('滚动条位置:' +document.body.scrollTop+",sssss========="+document.documentElement.scrollTop);
- console.log(document.body.scrollTop || document.documentElement.scrollTop)
- }
- window.onscroll = debounce(showTop,1000)
- </script>
平时开发中常遇到的场景:
- function throttle(fn,delay){
- let valid = true
- return function() {
- if(!valid){
- //休息时间 暂不接客
- return false
- }
- // 工作时间,执行函数并且在间隔期内把状态位设为无效
- valid = false
- setTimeout(() => {
- fn()
- valid = true;
- }, delay)
- }
- }
- /* 请注意,节流函数并不止上面这种实现方案,
- 例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
- 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
- */
- // 以下照旧
- function showTop () {
- var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
- console.log('滚动条位置:' + scrollTop);
- }
- window.onscroll = throttle(showTop,1000)
- let img = /<img/g
- let video = /<video/g
- bookInfo.content = bookInfo.content.replace( img, "<img style='max-width: 100%;'")
- .replace( video, "<video style='max-width: 100%;'")
js去除字符串空格
去除所有空格: str = str.replace(/\s*/g,"");
去除两头空格: str = str.replace(/^\s*|\s*$/g,"");
去除左空格: str = str.replace( /^\s*/, “”);
去除右空格: str = str.replace(/(\s*$)/g, "");
str为要去除空格的字符串,实例如下:
var str = " 23 23 ";
var str2 = str.replace(/\s*/g,"");
console.log(str2); // 2323
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
能更快获取到服务端数据,减少页面 loading 时间;
ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。主要包括以下几个模块:
State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
一、安装
- 1、src目录下面新建一个vuex的文件夹
- 2、vuex 文件夹里面新建一个store.js
- 3、安装vuex
- cnpm install vuex --save
- 4、在刚才创建的store.js引入vue 引入vuex 并且use vuex
- import Vue from 'vue'
- import Vuex from 'vuex'
- Vue.use(Vuex)
二、 store.js
- import Vue from 'vue'
- import Vuex from 'vuex'
- Vue.use(Vuex)
- const store = new Vuex.Store({
- state: {
- token: "",
- },
- mutations: {
- setToken: ( state, token ) => {
- state.token = token
- }
- },
- actions: {
-
- },
- getters:{
- token: state => {
- if ( state.token ) {
- return state.token
- } else {
- return uni.getStorageSync("token")
- }
- }
- }
- })
- export default store
三、使用vuex
1.获取vuex中state的值
- 1.第一种就是
- this.$store.state.count (需要获取的属性值)
-
- 2.第二种就是
- import {mapState} form 'vuex'
-
- computed:{
- ...mapState(['count'])
-
- }
- 3.第三种就是
- computed:{
- count() {
- return this.$store.count
- }
-
- }
2. mutations vuex中用于改变state
1.只能通过mutations更改store数据,不可以直接操作store中的数据
2.通过mutations这种方式操作起来稍微繁琐一点,但是可以集中监控所有的数据变化
- mutations:{
- add(){
- state.count++
- },
- //mutations传参
- adds(state,step){
- state.count+=step
- },
-
- }
-
- //组件中使用
- 1.第一种方式
- methods:{
- numadd(){
- this.$store.commit('add')
- },
- numadd2(){
- this.$store.commit('adds',2)
- },
-
- }
- 2.第二种方式
- import {mapMutations} from 'vuex'
- methods:{
- ...mapMutations(['add','adds'])
- numadd(){
- this.add()
- },
- numadd2(){
- this.adds(20)
- },
- }
- //定义方法
- actions:{
- addasync (context){
- setTimeout(()=>{
- context.commit('add')
- },1000)
- }
-
- //异步方法传参
- addasyncs(context,step){
- setTimeout(()=>{
- context.commit('adds',step)
- },1000)
- }
- }
-
-
- //页面调用
- //1.第一种调用方式
- methods:{
- addasync(){
- this.$store.dispath('addasync')
- }
- addasyncs(){
- this.$store.dispath('addasyncs',5)
- }
- }
-
-
- //页面调用
- //2.第二种调用方式
- import {mapActions} from 'vuex'
- methods:{
- ...mapActions(['addasync','addasyncs'])
- add(){
- this.addasync()
- }
- adds(){
- this.addasyncs(5)
- }
- }
1.getter可以对store中已有的数据加工处理之后形成新的数据,类似vue的计算属性
2.store中数据发生变化,getter的数据也会跟着变化
- getteer:{
- showNum: count=>{
- return 'count的值为:'+state.count
- }
-
- }
-
- //组件中使用getter
- //第一种方式
- this.$store.getter.名称
- //第二种方式
-
- import { mapGetters } from 'vuex'
-
- computed:{
- ...mapGetters(['showNum'])
- }
SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。
服务端渲染 SSR 的优缺点如下:
(1)服务端渲染的优点:
更好的 SEO:因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
更快的内容到达时间(首屏加载更快):SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
(2) 服务端渲染的缺点:
更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
回流 (Reflow ):当render树中的一部分或者全部因为大小边距等问题发生改变而需要重建的过程叫做回流;
重绘(Repaint ):当元素的一部分属性发生变化,如外观背景色不会引起布局变化而需要重新渲染的过程叫做重绘。
例如修改背景颜色,字体颜色之类不影响布局的行为都只引发重绘。其余情况可以看做为是回流重绘,回流必将引起重绘,而重绘不一定会引起回流。 所以,我们需要很清楚,页面若发生回流则需要付出很高的代价(会发生卡顿甚至页面卡死)。
如何避免/减少回流和重绘。
如果需要频繁用JS操作DOM节点,可以使用 documentfragment ,这是一个纯增加性能的方法;
在CSS属性用法上,用 translate 代替 top ,因为 top 会触发回流,但是translate不会。所以translate会比top节省了一个layout的时间;
在CSS属性用法上,opacity 代替 visiability,会触发重绘(paint),但opacity不会。该用法只针对于独立图层上;
若用JS去修改CSS的,最好别频繁去操作DOM节点,最好把需要操作的样式,提前写成class,之后需要修改。只需要修改一次,需要修改的时候,直接修改className,做成一次性更新多条css DOM属性,一次回流重绘总比多次回流重绘要付出的成本低得多;(谷歌浏览器有这个缓冲 flush 机制,如果在某个周内进行多次操作的话,会缓冲一次修改。)
离线回流重绘,把需要回流重绘的节点,进行隐藏离线回流重绘,display:none;
每次访问DOM的偏移量属性的时候,例如获取一个元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之类的属性,浏览器为了保证值的正确也会回流取得最新的值,所以如果你要多次操作,最取完做个缓存。更加不要for循环中访问DOM偏移量属性,而且使用的时候,最好定义一个变量,把要需要的值赋值进去,进行值缓存,把回流重绘的次数减少;
尽量少用table布局,table布局的话,每次有单元格布局改变,都会进行整个tabel回流重绘;
如果需要使用动画,动画速度根据平滑度和回流重绘进行一下计算页面性能的平衡;
把需要频繁回流重绘的单独抽出去一个图层,使用 transform:translateZ(0) 或 will-change:transform 的Css属性都能实现;
启用GPU加速,使用transform:translateZ(0) / transform:translate3d(0,0,0) 对性能提升很大。但是GPU的不能滥用,因为开启这个加速,会造成图层过多,反而另render 树对图层合并的时间变长,适得其反。
重绘回流优化方案总结
上面列了这么多点,总结还是三点去优化:
避免使用触发重绘回流的CSS属性。
尽量减少JS操作修改DOM的CSS次数。
将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流影响只会在这个图层中。页面不能有过多图层。
解决方案:
1. 给父元素加边框border
2. 给父元素加内边距padding
3. 给父元素加overflow:hidden
- // 编写方法,实现冒泡
- function bubble(arr){ // 1:套一个函数的壳子,将参数传入
- //外层循环,控制趟数,每一次找到一个最大值
- 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 temp = arr[j];
- arr[j] = arr[j + 1];
- arr[j + 1] = temp;
- }
- }
-
- }
- return arr //2: 将执行完的结果返回就可以
- }
- var arr = [29,45,51,68,72,97];
- console.log(bubble(arr));//[2, 4, 5, 12, 31, 32, 45, 52, 78, 89]
-
-
- 先判断冒泡的时候,是不是数组,数组是否为空 Array.isArray()
- if (arr instanceof Array && arr.length > 1) {
-
- // 冒泡
-
- }
总结起来就是:
html语义化标签包括 body, article, nav, aside, section, header, footer, hgroup, 还有 h1-h6 address等。
header代表“网页”或者“section”的页眉,通常包含h1-h6 元素或者 hgroup, 作为整个页面或者一个内容快的标题。也可以包裹一节的目录部分,一个搜索框,一个nav,或者相关logo。
hgroup
元素代表“网页”或“section”的标题,当元素有多个层级时,该元素可以将h1
到h6
元素放在其内,譬如文章的主标题和副标题组合
footer
元素代表“网页”或任意“section”的页脚,通常含有该节的一些基本信息,譬如:作者,相关文档链接,版权资料。如果footer
元素包含了整个节,那么它们就代表附录,索引,提拔,许可协议,标签,类别等一些其他类似信息。
nav 元素代表页面的导航链接区域。用于定义页面的主要导航部分。
article 代表一个在文档,页面或者网站中自成一体的内容,其目的是为了让开发者独立开发或重用。
除了它的内容,article会有一个标题(通常会在header
里),一个footer
页脚。
section
元素代表文档中的“节”或“段”,“段”可以是指一片文章里按照主题的分段;“节”可以是指一个页面里的分组。section
通常还带标题,虽然html5中section会自动给标题h1-h6降级,但是最好手动给他们降级。
1、什么是闭包?
闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取另一个函数作用域的变量的函数)
闭包有哪些坑点?
坑点1: 引用的变量可能发生变化
坑点2: this指向问题
- var object = {
- name: ''object",
- getName: function() {
- return function() {
- console.info(this.name)
- }
- }
- }
- object.getName()() // underfined
- // 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
坑点3:内存泄露问题
- function showId() {
- var el = document.getElementById("app")
- el.onclick = function(){
- aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
- }
- }
-
- // 改成下面
- function showId() {
- var el = document.getElementById("app")
- var id = el.id
- el.onclick = function(){
- aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
- }
- el = null // 主动释放el
- }
使用闭包的注意点
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
通常说闭包就是函数嵌套函数 就是为可重用变量 保证变量不受污染的一种机制 说到变量 那么变量又分为全局变量和局部变量
而全局变量 可重用 但是容易污染 局部变量 函数内部使用 不可重用 不会收到污染 那么就出现了闭包 外部函数包裹内部变量和函数
通过 return 内层函数返回给外部 我们通过调用外层函数获取内层函数的值 因外层函数被调用后 内层函数操控着外层函数包裹的对象
所以导致这个变量无法被 垃圾回收机制回收 所以形成闭包
当用户输入网址点击回车后:
浏览器开辟出一条进程接收url
在发送请求前会判断是否触发缓存,有缓存读取缓存中的数据(欲知详情请移步)
如果没有缓存,浏览器向 DNS域名服务器查询,把域名解析成IP找到相应主机(配置host可跳过dns域名解析,加速网页渲染)
三次握手建立tcp连接,发送http请求
服务器响应请求,四次挥手返回数据并断开连接。
浏览器获取到 html后
解析HTML,生成 DOM 树
解析 CSS,生成 CSS 规则树
合并 DOM 树和 CSS 规则树,生成 render 树
布局 render 树(Layout/Reflow), 负责元素尺寸、位置的计算
绘制render树(Paint),绘制页面像素信息
浏览器将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。
1.作用域是针对变量的,比如我们创建了一个函数,函数里面又包含了一个函数,那么现在就有三个作用域
全局作用域==>函数1作用域==>函数2作用域
作用域的特点就是,先在自己的变量范围中查找,如果找不到,就会沿着作用域往上找。
如:
- var a = 1;
- function b(){
- var a = 2;
- function c(){
- var a = 3;
- console.log(a);
- }
- c();
- }
- b();
最后打印出来的是3,因为执行函数c()的时候它在自己的范围内找到了变量a所以就不会越上继续查找,如果在函数c()中没有找到则会继续向上找,一直会找到全局变量a,这个查找的过程就叫作用域链。
不知道你有没有疑问,函数c为什么可以在函数b中查找变量a,因为函数c是在函数b中创建的,也就是说函数c的作用域包括了函数b的作用域,当然也包括了全局作用域,但是函数b不能向函数c中查找变量,因为作用域只会向上查找。
2.那么什么是原型链呢?
原型链是针对构造函数的,比如我先创建了一个函数,然后通过一个变量new了这个函数,那么这个被new出来的函数就会继承创建出来的那个函数的属性,然后如果我访问new出来的这个函数的某个属性,但是我并没有在这个new出来的函数中定义这个变量,那么它就会往上(向创建出它的函数中)查找,这个查找的过程就叫做原型链。
Object ==> 构造函数1 ==> 构造函数2
就和css中的继承一样,如果自身没有定义就会继承父元素的样式。
- function a(){};
- a.prototype.name = "追梦子";
- var b = new a();
- console.log(b.name); //追梦子
1.undefined
声明一个变量但未初始化,这个变量的值就自动被赋予undefined值
2.null
一个空指针对象
- console.log(null==undefined); //true 因为两者都默认转换成了false
- console.log(typeof undefined); //"undefined"
- console.log(typeof null); //"object"
- console.log(null===undefined); //false "==="表示绝对相等,null和undefined类型是不一样的,所以输出“false”
为什么会有Promise出现?
在实际的使用中,有非常多的应用场景我们不能立即知道应该如何继续往下执行。最常见的一个场景就是ajax请求。通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去
当出现第三个ajax(甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。这场灾难,往往也被称为回调地狱。
因此我们需要一个叫做Promise的东西,来解决这个问题。当然,除了回调地狱之外,还有一个非常重要的需求:为了代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来。上面的写法,是完全没有区分开,当数据变得复杂时,也许我们自己都无法轻松维护自己的代码了。这也是模块化过程中,必须要掌握的一个重要技能,请一定重视
Promise对象有三种状态,他们分别是:
这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。在Promise对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理Promise的状态变化
Promise 中reject 和 catch 处理上有什么区别
reject 是用来抛出异常,catch 是用来处理异常reject 是 Promise 的方法,而 catch 是 Promise 实例的方法reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch网络异常(比如断网),会直接进入catch而不会进入then的第二个回调
必须使用 key ,且不能是 index 和 random ;
原因在于,在 vue 的 diff 算法中,通过对 tag 和 key 来判断是否为相同节点 sameNode ,如果是相同节点,则会尽可能的复用原有的 DOM 节点。
使用 key 的好处是:减少渲染次数,提升渲染性能。
keep-alive的好处:1.可以缓存组件。2.频繁切换的时候,不需要重复渲染。
1、首先在路由中的mate属性中记录keepAlive,如果需要缓存则置为true。
- path:'/index',
- name:''index',
- component:()=>import('../../index/index'),
- meta:{keepAlive:true}
2、在创建router实例的时候加上scrollBehavior方法(keepAlive才会生效)。
- let router=new Router({
- mode:"hash",//1、hash哈希:有#号。2、history历史:没有#号
- base:process.env.BASE_URL, //自动获取根目录路径
- scrollBehavior:(to,from,position)=>{
- if(position){
- return position
- }else{
- return {x:0,y:0}
- }
- },
3、需要缓存的router-view包上keep-alive(要有两个router-view,一个是缓存的时候显示,一个是不缓存的时候显示,有的时候不需要缓存)。
- <keep-alive>
- <router-view v-if="$router.meta.keepAlive"></router-view>
- </keep-alive>
- <router-view v-if="!$router.meta.keepAlive"></router-view>
- <template>
- <div>
- <button @click="changeState('A')">A</button>
- <button @click="changeState('B')">B</button>
- <button @click="changeState('C')">C</button>
-
- <keep-alive> <!-- tab 切换 -->
- <KeepAliveStageA v-if="state === 'A'"/> <!-- v-show -->
- <KeepAliveStageB v-if="state === 'B'"/>
- <KeepAliveStageC v-if="state === 'C'"/>
- </keep-alive>
- </div>
- </template>
-
- <script>
- import KeepAliveStageA from './KeepAliveStateA'
- import KeepAliveStageB from './KeepAliveStateB'
- import KeepAliveStageC from './KeepAliveStateC'
-
- export default {
- components: {
- KeepAliveStageA,
- KeepAliveStageB,
- KeepAliveStageC
- },
- data() {
- return {
- state: 'A'
- }
- },
- methods: {
- changeState(state) {
- this.state = state
- }
- }
- }
- </script>
缺点:
1.遍历来源不明确,不利于阅读。
2.多mixin可能会造成命名冲突。
3.mixin和组件可能出现多对多的关系,复杂度较高,做好只用1个吧。
特点:
1.组件data中的数据与混入中data的数据冲突时,取组件内数据
2.使用相同的生命周期函数时,混入和组件内的会合并成一个数组,都会被调用,但是混入的函数会优先调用
3.值为对象的选项,例如methods,components,会形成一个对象,都会执行, 但是当混入和组件内有相同的键名时,
取组件内对象的键值对
4.当有局部混入和全局混入冲突时,取局部混入的数据
(1)如何判断一个对象是不是空对象
Object.keys(obj).length === 0
(2)如何判断变量是一个String类型
typeof(obj) === "string"
typeof obj === "string"
obj.constructor === String
当对象查找一个属性的时候,如果没有在自身找到,那么就会查找自身的原型,如果原型还没有找到,那么会继续查找原型的原型,直到找到 Object.prototype 的原型时,此时原型为 null,查找停止。这种通过 通过原型链接的逐级向上的查找链被称为原型链
一个对象可以使用另外一个对象的属性或者方法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样根据原型链的规则,如果查找一个对象属性且在自身不存在时,就会查找另外一个对象,相当于一个对象可以使用另外一个对象的属性和方法了。
1.浅拷贝
- Array.from(array)//创建一个新数组
- let [...spread]= [12, 5, 8, 130, 44];
2.深拷贝
第一种(这是因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数)
JSON.parse(JSON.stringify())
第二种
- function deepCopy(obj) {
- //是否是数组或对象
- if (typeof obj !== 'object') return;
-
- var newObj = obj instanceof Array ? [] : {};
-
- for (var key in obj) {
-
- // 不遍历原型链上的属性,只遍历自身属性
- if (obj.hasOwnProperty(key)) {
-
- // 如果值是对象,就递归一下
- if (obj[keys] && typeof obj[key ] === "object") {
-
- newObj[key] = obj[key] instanceof Array ?[] : {};
- // 如果是引用数据类型,会递归调用
- newObj[key] = deepCopy(obj[key]);
- } else {
-
- // 如果不是,就直接赋值
- newObj = obj[keys];
-
- }
- }
- }
- return result;
- }
-
-
- 在全局范围内,this指向全局对象(浏览器下指window对象)
-
- 对象函数调用时,this指向当前对象
-
- 全局函数调用时,应该是指向调用全局函数的对象。
-
- 使用new关键字实例化对象时,this指向新创建的对象
-
- 当用apply和call上下文调用的时候指向传入的第一个参数
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。
mvc和mvvm其实区别并不大。都是一种设计思想。主要就是mvc中Controller演变成mvvm中的viewModel。mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
区别:vue数据驱动,通过数据来显示视图层而不是节点操作。
场景:数据操作比较多的场景,更加便捷
方法一:在接口获取到数据的时候,利用数组方法map先处理数据,再直接渲染
方法二:利用vue提供的filters钩子,实现数据的过滤处理再渲染处理过后的内容。
- // lists数组为:lists =[{id:1,name:'Seven'},{id:2,name:'Jack Jones'}]
- <ul>
- <li v-for="item in lists" :key="item.id">{{item.name | filterOne | filterTwo('同学') }}</li>
- </ul>
- filters:{ //钩子
- filterOne(val){ //过滤器函数的名字filterOne,val是传递过来的参数
- return val.toUpperCase() //操作之后将结果返回出去
- },
- filterTwo(val,unit) {
- //此处val是经过上一个过滤器filterOne操作之后的返回的结果,unit是参进来的参数
- return val+unit
- }
- }
这里就涉及到 Vue 一个很重要的概念:异步更新队列(JS运行机制 、 事件循环)。
Vue 在观察到数据变化时并不是直接更新 DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。
在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。
然后,在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。
所以如果用 for 循环来动态改变数据100次,其实它只会应用最后一次改变,如果没有这种机制,DOM就要重绘100次,是一个很大的开销,损耗性能。
1.先引入 后设置
- import Index from '@/views/Index.vue'
-
- { path: '/',
- name: 'Index',
- component: Index
- },
2.路由懒加载
- { path: '/',
- name: 'Index',
- component: () => import('@/views/Index'),
- },
两种的区别:
先引入 后设置:直接先导入,在设置,会导致所有引入的文件全部打包到1个.js文件中
路由懒加载:会导致所有引入的文件单个打包到每一个.js文件中
如果我们使用路由懒加载, 他会单独把单独的路由组件加载进来。
路由懒加载:
缺点: 每次加载都需要等待
优点:只有用户访问时才去请求当前的路由
直接引入使用:
缺点:首页加载缓慢
优点:一次打开后,后续就不需要在请求资源了。
总结:当页面比较频繁切换的时候 建议 先引入在使用, 如果一些不频繁的页面通过路由懒加载的方式使用。
- import Vue from 'vue'
- import {
- Button,
- Tabbar,
- TabbarItem,
- Form,
- Field,
- NavBar,
- } from 'vant'
-
- //第一种方式
- Vue.use(Button)
- Vue.use(Tabbar)
- Vue.use(TabbarItem)
- Vue.use(Form)
- Vue.use(Field)
- Vue.use(NavBar)
-
- //第二种方式 虽然代码量差不多 建议使用第二种
- const cps = [Button,
- Tabbar,
- TabbarItem,
- Form,
- Field,
- NavBar,
- Toast,
- Tabs,
- Tab,]
- cps.forEach(cp => Vue.use(cp))
npm install terser-webpack-plugin -D
配置vue.config.js
- module.exports = {
-
- configureWebpack: (config) => {
- // 在webpack的配置对象 config的 optimization属性的minimizer数组的第一个元素的options中设置....
- // 在打包之后的js中去掉console.log
- config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
- },
- publicPath: './'
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。