必备-14.vue3

必备-14.vue3

vue3和vue2的版本

npm i vue@lasted:下载的是vue2

npm i vue@next:下载的是vue3

  • 老版本
    • vue2
    • vuex3
    • vue-router3
    • PC端组件库:element-ui 2
    • 移动端组件库:vant2;https://vant-contrib.gitee.io/vant/v2/#/zh-CN/quickstart
  • 新版本
    • vue3
    • vuex4
    • vue-router4
    • PC端组件库:element-plus 1
    • 移动端组件库:vant3:https://vant-contrib.gitee.io/vant/v3/#/zh-CN/quickstart
    • npm i vuex@next vue-router@next element-plus

vue3的安装使用

  • 一、安装vue3:npm i vue@next
  • 二、安装vue3版本的项目
  • 三、安装vue-router、vuex
  • 四、安装less@3、less-loader@7
  • 五、配置vue.config.js,与vue2相同

vue3与vue2的十大区别

  • 版本号不同

  • 引用的vuex/vue-router版本不同

  • vue3的APP.vue的《template》 中支持多个根节点

  • 区别一vue3中推崇使用函数式编程:各种创建实例,都调用函数来创建,我们从vue中解构出各种各样的函数来使用

  • 区别二vue3重写了虚拟DOM的实现:(Performance)(跳过静态节点,只处理动态节点)

    • update性能提高1.2-2倍
    • SSR(视图渲染)速度提高2-3倍
  • 区别三vue3可以将无用模块"裁掉",仅打包需要的:(Tree shaking)【webpack新版本的】

  • 区别四vue3支持多个根节点(Fragment)【文档碎片】

  • 区别五vue3有传送门:(<Teleport to="#fotter">),可以把组件内部的部分内容挂载到除#app的容器中

  • 区别六vue3中可以创建异步组件:(<Suspense>)嵌套一个异步组件,可以在获取到数据之前加载loading效果

  • 区别七TypeScript:可以更好的支持TypeScript语法

  • 区别八vue3支持把组件中的某些内容以图形的方式绘制到canvas画布上😦Custom Renderer API自定义渲染器API)

  • 区别九vue3使用的Composition API:(Composition API聚合API):vue2遵循options API,vue3遵循Composition API

    • options API:数据根据功能划分到data、methods、computed、watch区域(分散式API)
    • Composition API:vue3中所有的数据方法都放到了setup()方法中,全部组合到了一起,没有细分(聚合式API)
  • 区别十vue3中的响应式数据劫持,基于Proxy实现的:vue3中的响应式数据劫持,不再基于vue2的Object.defineProperty(),而是基于ES6内置的Proxy:但是不兼容IE

defineProperty&Proxy

  • vue2中的响应式原理

    • 基于Object.defineProperty()方法做get和set劫持的

      • 依次迭代对象中的每一项,给每一项分别做数据劫持
      • 对深层级别的对象,我们需要基于递归的方式,再次进行劫持
      • 对于数组的每一个索引项不做劫持,但是修改其原型指向,指向自己重构的原型对象【包含7个方法,执行这7个方法除了修改数据之外,还会通知视图重新渲染arr->自己重构的原型对象->Array.prototype->Object.prototype
      //一:创建重写了七个数组方法的对象,并让所有的数组的原型链都指向这个对象:主要实现的目的:通知视图渲染
      const proto = {
                  push(...params) {
                      Array.prototype.push.call(this, ...params);
                      // 通知视图渲染
                  },
                  pop() {  },
                  shift() { },
                  unshift() { },
                  splice() { },
                  sort() { },
                  reverse() { }
              };
      		//二、让重写六个方法的对象的原型链指向Array的原型
              Object.setPrototypeOf(proto, Array.prototype);
      //------------------------------创建监听函数observe
              const observe = function observe(obj) {
                   // 如果是一个数组,每一个索引项无需做劫持,但需要改变这个数组的原型->proto
                  if (Array.isArray(obj)) {
                      Object.setPrototypeOf(obj, proto);
                      return;
                  }
                  //如果不是对象直接返回
                  if (!isPlainObject(obj)) return;
                  // 把obj进行深拷贝
                  let proxyObj = JSON.parse(JSON.stringify(obj));
                  // 迭代对象中的每一项,给每一项做劫持
                  let keys = Reflect.ownKeys(obj);
                  keys.forEach(key => {
                      //对对象中各属性做get、set拦截
                      Object.defineProperty(obj, key, {
                          get() {
                              return Reflect.get(proxyObj, key);
                          },
                          set(newValue) {
                              if (newValue === Reflect.get(proxyObj, key)) return;
                              Reflect.set(proxyObj, key, value);
                              // 通知视图重新渲染
                          }
                      });
                      if (isPlainObject(obj[key]) || Array.isArray(obj[key])) {
                          observe(obj[key]);
                      }
                  });
              };
              observe(obj);
      
      
  • vue3中的响应式原理:基于ES6的Proxy类来实现,为啥这么做

    • 因为Proxy各方面表现都比Object.defineProperty好一些,【除了不兼容IE浏览器

    • Proxy可以直接代理整个对象,无需依次迭代对象每一项做get/set劫持【性能好

    • 对于数组来讲,直接基于Proxy代理即可,无需像vue2中一样,再自己重写7个方法【对数组友好

    • 基于defineProperty只能做get/set劫持,但是基于基于Proxy可以做的劫持方式很多:【功能强大

      • get/set劫持
      • getPrototypeOf()劫持
      • setProot ypeOf()劫持
      • defineProperty()劫持
      • deleteProperty()劫持
      • has劫持
      • ownKeys()劫持
    • 相同点:都是需要基于递归的方式,对“对象”深层次进行劫持代理

    let proxyObj=new Proxy(obj,{
    	get(target,key){
    		return target[key];
    	},
    	set(target,key,value){
    		if(target[key]===value) render;
    		target[key]=value;
    		//控制视图重新渲染
    	}
    })
    

vue2生命周期顺序

  • 初始化props
  • beforeCreate[实例还没创建好,也就是不用this]
    • 挂载数据/方法
  • created[可以用this了]
  • beforeMount
    • 渲染
  • mounted[可以获取DOM了]

setup()钩子函数

  • setup(props):钩子函数是Composition API的入口点,组件内的大部分内容都需要在这处理
    • 发生在初始化props之后,以及beforeCreate之前,函数中没有this(this是undefined)
    • 可以基于形参props接收初始化之后的属性值
      • 属性值是一个对象,而且是基于Proxy代理后的响应式对象
      • 并且属性是只读的(readonly),当我们去修改某个属性值的时候,控制台会提示警告

使用setup中的数据

  • 函数执行返回一个对象,对象中包含啥,那么这些东西就可以直接在视图中进行渲染和使用了

  • <div>{{name}}</div>//-》<div>hahaha</div>
    setup(props){
        //这里的supNum和change不是响应式的
        supNum:0;
        const change=()=>{
            supNum++;
        }
    	return {
    		name:"hahaha",
            supNum,
            change
    	}
    }
    

vue3中的10+大响应式API

  • vue3中创建响应式数据不能直接在setup中创建,需要两个APIref/reactive
  • vue3中创建的响应式数据分为两大类:
    • ref、computed:是基于RefImpl类创建的实例,它的底层是由**Object.defineProperty()**方法做的劫持
    • props、reactive:是基于Proxy类创建的实例,它的底层是由Proxy类做的劫持:默认会有get/set/has/ownkeys/defineProperty五个的劫持

ref:创建状态值

  • 创建一个状态值就得执行一次ref

  • <script>
    	import {ref,reactive,computed} from "vue"
    	
    setup(props){
    		/*响应式系统API之一:ref
    			let xxx=ref([initialValue]);创建一个响应式状态
    			xxx是RefImpl类的实例
                */
    		let supNum=ref(100),
    			oppNum=ref(50);
           		const change=(type)=>{
                    type==="sup"?supNum.value++:oppNum.value++;
                }
            /* 计算属性
                let xxx=computed([getter函数])
                它是ComputedRefImpl类的一个实例对象,和RefImpl类似,也是对其value属性做的数据劫持,我们操作的也是xxx.value属性
                默认情况下创建的计算属性是只读的,所以:xxx.value=100会抛出警告
                computed({get(){},set(newValue){}}),基于这种方式可以设置computed计算属性的可写性
             */
            let ratio=computed(()=>{
                let total=supNum.value+oppNum.value;
                return total===0?'----':(supNum.value/total*100).toFixed(2)+"%";
            })
    
    
    
            return{
                supNum,
                oppNum,
                change,
                ratio
            }
    
    </script>
    
    • 语法let xxx=ref([initialValue]);创建一个响应式状态xxx是RefImpl类的实例

    • ref实现的响应式数据是基于defineProperty()做劫持的(不是基于Proxy),

    • 并且是给RefImpl实例对象中的xxx.value属性处理的数据劫持,所以我们后期操作的是xxx的value属性

      • 获取:xxx.value
      • 赋值:xxx.value=100
    • 在视图渲染的时候,我们用{{xxx}}调用数据时,无需自己写.value,模板编译的时候会自动取RefImpl对象的value值进行渲染!!

reactive:创建状态值

  • 可以同时生成多个响应式状态值

  • 
    <script>
    import { ref, reactive, computed,toRefs} from "vue";
    export default {
      name: "Vote",
      props: ["title"],
      setup(props) {
        let state = reactive({
          supNum: 100,
          oppNum: 50,
        });
        const change = (type) => {
            //不能对state进行解构赋值,因为我们获取的supNum和oppNum是原始值
            type ==="sup"?state.supNum++:state.oppNum++;
        };
    
        let ratio = computed(() => {
          let total = state.supNum+ state.oppNum;
          return total === 0? "----": ((state.supNum / total) * 100).toFixed(2) + "%";
        });
    
        return {
          ...toRefs(state),//调用toRefs()将Proxy对象转为refs
          change,
          ratio,
        };
      },
    };
    </script>
    
  • 基于ES6中的Proxy实现响应式代理,而且也进行了深层次的劫持和代理

  • 语法let xxx= reactive({x:100,y:100})

  • 获取state.xxx

  • 赋值state.xxx=100

  • 处理了Proxy的这些劫持函数:get/set/has/ownkeys/deleteProperty

  • 如果我们在js中把state状态解构赋值处理,一定要了解下面这几点:

    • 如果只是获取使用,则没有任何问题
    • 但是如果用解构后的变量去进行修改,不会触发代理对象的set函数,实现不了通知视图重新渲染
    • 所以轻易不要解构

toRefs:转多个状态值类型

  • 将reactive创建的响应式状态值,转为基于ref管理的refImpl对象

  • 目的:在视图中渲染RefImpl对象比较简单【返回一个对象,里面包含,把state中每一项都变为单独的ObjectRefImpl的实例对象】

  • 语法:

    • let state=reactive({x:100,y:200})
      let refs=toRefs(state);//将state中的x和y转为refImpl对象
      

toRef:转单个状态值类型

  • 与toRefs相同,转单个reactive创建的响应式状态值

  • 语法

    • let state=reactive({x:100,y:200})
      let ref=toRefs(state.x);//将state中的x和y转为refImpl对象
      

unRef

  • 把响应式数据变为非响应式的

computed:计算属性

  • vue3推崇函数式编程,我们想做计算属性,需要在vue中解构出computed函数

  • 语法let xxx=computed([getter函数])

  • 它是ComputedRefImpl类的一个实例对象,和RefImpl类似,也是对其value属性做的数据劫持,我们操作的也是xxx.value属性

  • 默认情况下创建的计算属性是只读的,所以:xxx.value=100会抛出警告

  • computed({get(){},set(newValue){}}),基于这种方式可以设置computed计算属性的可写性

/* 计算属性
            let xxx=computed([getter函数])
            它是ComputedRefImpl类的一个实例对象,和RefImpl类似,也是对其value属性做的数据劫持,我们操作的也是					xxx.value属性
            默认情况下创建的计算属性是只读的,所以:xxx.value=100会抛出警告
            computed({get(){},set(newValue){}}),基于这种方式可以设置computed计算属性的可写性
         */
       let ratio=computed(()=>{
            let total=supNum.value+oppNum.value;
            return total===0?'----':(supNum.value/total*100).toFixed(2)+"%";
        })
       

watch:监听器

  • watch可以指定监听哪一个状态、属性,在状态更改后触发指定的函数执行

    • 默认情况下,第一次渲染组件,并不会执行监听函数【可以配置】
    • 默认不是进行深层次监听【可以配置】
  • 语法

    • watch(RefImpl对象实例,函数):监听某一个Ref对象

    • watch(state,函数):监听所有基于reactive创建的状态对象

    • watch(()=>{state.xxx},函数):如果监听state的某一个状态,则必须携程函数

    • 如果想配置状态值:watch(监听的对象,执行的函数,配置项)

      //配置watch的状态值,
      watch(state,()=>{},
           {
          immdiate:true,
          deep:true
      })
      

watchEffect:监听器

  • 语法wathEffect([callback])

    • 第一次渲染组件,[callback]触发执行一次

    • 自动根据函数中的代码建立相关状态值的依赖(用到啥就依赖啥),当依赖的值发生改变,[callback]还会重新执行

    • 用处:setup()函数只会在组件第一次渲染的时候执行一次,当组件更新的时候,setup()并不会执行,但是无论是计算属性还是监听器都会根据依赖的信息是否发生变化,重新执行,获取最新的状态值

    • watchEffect(()=>{
      	state.supNum
      })
      

readonly:只读

  • 把一个状态值变为只读的
  • let copy=readonly(value):copy就是只读的

isRef

isReactive

  • 判断状态值是不是使用reactive()创建的

isReadonly

  • 判断状态值是不是Readonly类型的实例

isProxy

  • 判断状态值是不是Proxy类型的实例:reactive、props生成的数据都是Proxy的实例

周期函数

  • vue2中的beforeCreate和created由==setup()==函数代替了
  • beforeMount->onbeforeMount
  • Mounted->onMounted
  • beforeUpdate->onBeforeUpdate
  • updated->onUpdated
  • beforeDestory->onBeforeUmount
  • destoryed->onUnMounted

DOM元素操作

  • vue3中的ref将创建状态值与绑定ref的DOM元素组合在了一起

  • 用法

    //1、创建一个ref状态值,值为null
        let myDom=ref(null);
    //2、导出该状态值
    	return{
            myDom
        }
    //3、在标签的ref中引用
    	<h1 ref="myDom">aaa</h1>
    //4、myDom绑定的value值就是这个标签
    	console.log(myDom.value)
    

小案例

知乎日报

  • 技术栈

    • @vue/cli、vue3、vuex4、vue-router4、vant3、axios(qs)...

    • postcss-pxtorem、amfe-flexible移动端适配

      • postcss-pxtorem:我们以后写样式都写px,这个插件会把我们写的px转换为rem
      • amfe-flexible:自动设置根元素字体大小
    • less & less-loader:样式编码规范

    • babel-plugin-import:按需导入UI组件库的

  • 需要完成的页面

    • 首页:新闻列表、头部、轮播图… /
    • 详情页:文章内容、底部操作栏 /detail/:id
    • 个人中心 /person
    • 收藏列表 /store
    • 登录页面 /login
    • 修改个人信息页 /update
  • 开始搭建项目的架子

    • 基于@vue/cli创建项目,&配置vue.config.js & 处理浏览器兼容 & 安装需要的模块
    • 在SRC目录中:
      • 配置vuex
      • 配置vue-router
      • 配置api
    • 配置响应式布局方案&导入UI组件库

vant

vant中的响应式布局

  • vant UI组件库本身是按照 375px的设计稿设计的
  • 所以 vant中写的所有样式尺寸(单位是px),按照375的设计稿量出来的
    • amfe-flexible 使用插件 ,会按照375的设计稿,把html font-size设置为 37.5px
      • 我们的目的是把 写死的px单位 除以 37.5 都转换为rem
    • 我们想用amfe-flexible,需要在main.js中导入这个插件
    • postcss-pxtorem:将所有的px除以37.5再转为rem
  • 真实项目中,设计师给我们的设计稿是750px的,可以缩小一倍,按照vant375px的设计稿设计
    • 其他设计稿,可以在postcss.config.js中的设置缩小的比例
// postcss.config.js
module.exports = {
    plugins: {
      // postcss-pxtorem 插件的版本需要 >= 5.0.0
      'postcss-pxtorem': {
        rootValue({ file }) {
            // 如果是750的设计稿,就把根节点的像素设为75px,
            //之后设计稿上是多大px,我们就写多大px,postcss就会除以75给我们缩小
          return file.indexOf('vant') !== -1 ? 37.5 : 75;
        },
        propList: ['*'],
      },
    },
  };

vue3的优化

  • Object.freeze():对不需要做深劫持的数据做冻结,使浏览器不需要再花时间去做迭代监听,提升性能

  • v-lazy:将img的src改为v-lazy,实现图片懒加载

  • IntersectionObserver:做滚动监听,延迟加载列表中的数据

    • //第一步:创建监听器:监听器就像一个事件池,可以同时监听多个DOM元素
      	//changes里面存放的是监控的DOM对象的数组
      le ob=new IntersectionObserver(async (changes)=>{
          //我们只监听了一个Dom节点
          let item=changes[0];
          	//当监听的对象出现在页面中时,isIntersecting会改为true
          if(item.isIntersecting){
              //自己想做的事
          }
      })
      	let loadMore=ref(null);
      	//只有当页面完成挂载时,才去监听这个DOM节点
      onMounted(()=>{
      	//判断是否拿到了loadMore,如果没拿到,监听器监听不了
          	if(!loadMore.value) return;
          //监听DOM元素
          	ob.observe(loadMore.value)
      })
      	//当组件触发销毁时,内存中的监听器并不会销毁,需要手动销毁
      onbeforeUnMounted(()=>{
          	if(!loadMore.value) return;
          //销毁DOM元素,优化性能
          	ob.unobserve(loadMore.value)
      })
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值