Blogger Information
Blog 54
fans 6
comment 31
visits 107493
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
vuejs框架入门和原生JS解构分析
吾逍遥
Original
5887 people have browsed it

一、如何认识vuesjs框架和原生JS的关系

本课以前,我虽然也进行项目开发快半年了,但其实不理解原生JS、jquery函数库和vuejs等框架的区别,只知道学了一个又一个,只到老师在授课中用原生JS来解构分析它们,才知道它们是基于原生JS进行了封装,为开发者提供更好的使用体验。前端这些函数库和框架都支持原生JS的操作,如老师在模板语法中使用原生JS的方法就给我很大启发,一下子对前端的JS有了系统的认识,在解决问题时也不迷茫了,正如我的博文https://www.php.cn/blog/detail/24847.html对购物车就用原生JS、jquery和vuejs三种方法实现了。下步自己还是深入原生JS学习,也许以前对它不重视,现在知道它的强大才越来越有兴趣了。

二、MVC和MVVM

官方描述:
Vue是一套用于构建用户界面的渐进式框架,Vue 被设计为可以自底向上逐层应用,Vue 的核心库只关注视图层.

虽然官方的描述比较难懂,其实它的核心就是视图层,位于MVVM的ViewModel位置,它对View和Model提供了双向绑定,能将二者变化反应到彼此,从而实现了数据联动,给我的第一感觉最大的特点是它关注数据、进行了数据双向绑定。那么MVVM又是什么呢?

mvcmvvm

MVC和MVVM:

  • MVC全名是Model View Controller,如图,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
    • M层处理数据,业务逻辑等
    • V层处理界面的显示结果
    • C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层
  • MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。它的由来是MVP和模式和微软的WPF结合的演变过来的新型架构框架。(题外话,不得不感慨微软在Web领域影响无处不在,尽管没什么独立优秀产品,如CSS中box-sizing的IE盒子,WPF对MVVM,哈哈我是微软Fans,对苹果不感冒)
  • MVC和MVVM比较:
    • 同: Model和View
      • Model :数据对象 ,同时,提供本应用外部对应用程序数据的操作的接口,也可能在数据变化时发出变更通知。Model不依赖于View的实现,只要外部程序调用Model的接口就能够实现对数据的增删改查。
      • View:UI层 ,提供对最终用户的交互操作功能,包括UI展现代码及一些相关的界面逻辑代码。
    • 异: 差异在于如何粘合View和Model,实现用户的交互操作以及变更通知
      • MVC中Controller: Controller接收View的操作事件,根据事件不同,或者调用Model的接口进行数据操作,或者进行View的跳转,从而也意味着一个Controller可以对应多个View。Controller对View的实现不太关心,只会被动地接收,Model的数据变更不通过Controller直接通知View,通常View采用观察者模式监听Model的变化。
      • MVVM中ViewModel: 注意这里的“Model”指的是View的Model,跟MVVM中的一个Model不是一回事。所谓View的Model就是包含View的一些数据属性和操作的这么一个东东,这种模式的关键技术就是数据绑定(data binding),View的变化会直接影响ViewModel,ViewModel的变化或者内容也会直接体现在View上。这种模式实际上是框架替应用开发者做了一些工作,开发者只需要较少的代码就能实现比较复杂的交互。
  • MVC->MVP->MVVM演进过程: MVC -> MVP -> MVVM 这几个软件设计模式是一步步演化发展的,MVP 隔离了MVC中的 M 与 V 的直接联系后,靠 Presenter 来中转,所以使用 MVP 时 P 是直接调用 View 的接口来实现对视图的操作的,这个 View 接口的东西一般来说是 showData、showLoading等等。M 与 V已经隔离了,方便测试了,但代码还不够优雅简洁,所以 MVVM 就弥补了这些缺陷。MVVM 是从 MVP 的进一步发展与规范,在 MVVM 中就出现的 Data Binding 这个概念,意思就是 View 接口的 showData 这些实现方法可以不写了,通过 Binding 来实现。

三、vuejs的基础入门

上面解释那么多,下面终于进入正题了,下面是vuejs的入门知识。首先要按官方教程在页面中 下载vuejs或引用vuejs ,然后在script中用 new调用构造函数创建vue对象(实例) 的,这点 要与jquery区别开来 (jquery已经创建了jQuery全局对象,没提供构造函数,不可new创建)。

1、挂载点即el属性

  1. var app = new Vue({
  2. el: '#app',
  3. });

上面就是官方提供的挂载演示,挂载属性就是el,全称是element,它明确了 vuejs作用域 ,就是vuejs会对该元素接管,对它后代元素中进行vuejs的解析,如数据双向绑定等。

这里我有个误区就是以为它只能是ID,采用’#ID’来挂载,老师在演示时直接使用document.querySelector()来挂载,再查官方文档才发现它对el已经有了明确的说明

el官方说明:

  • 类型:string | Element
  • 限制:只在用 new 创建实例时生效。
  • 提供一个在页面上 已存在的DOM元素 作为Vue实例的挂载目标。可以是 CSS选择器 ,也可以是一个 HTMLElement实例
  • 在实例挂载之后,元素可以用 vm.$el 访问
  • 如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount()手动开启编译。
  • 提供的元素只能作为挂载点。不同于 Vue 1.x,所有的挂载元素会被Vue生成的DOM替换 。因此不推荐挂载root 实例到 \<html> 或者 \<body> 上。
  1. <div class="container">
  2. <p>Hello VueJS</p>
  3. </div>
  4. <script>
  5. // 1.挂载点el属性
  6. const vm = new Vue({
  7. el: '.container',
  8. });
  9. console.log('vm.$el => ', vm.$el);
  10. console.log('挂载点遍历 => ', vm.$el.firstChild);
  11. // 尝试原生JS增加元素
  12. const pl = document.createElement('p');
  13. pl.innerHTML = 'Hello woxiaoyao';
  14. vm.$el.insertBefore(pl, vm.$el.firstChild);
  15. </script>

el

第一个要点: 挂载方式推荐CSS选择器,这里就好比jquery的\$()的选择器。当然也可以使用document.querySelector或document.getElementXXX等方法获取的HTMLElement对象(实例)。这也许就是函数库或框架与原生JS联系的纽带了。

第二个要点: el只能在new创建实例时使用,此时将立即进入编译过程,vue接管该元素。若没有则是”未挂载”状态,需要使用vm.$mount(selector|Element)手动挂载,它的参数和el值是一样的,字符串型,支持CSS选择器或HTMLElement对象,手动挂载后vue将接管挂载的元素。

第三个要点: 对于挂载元素可以用vm.$el来访问,它是原生JS的HTMLElement对象,当然也可以直接用原生JS或\$()来访问,至于增删改元素操作也没问题,上例中就演示了原生JS的插入操作。这里不得不说下Virtual DOM,因为官方说明挂载元素会被Vue生成的DOM替代,所以我以为Vue的dom元素就不是HTML的dom元素,其实还是dom元素。

下面说下我对Virtual DOM和HTML DOM的理解。很重要哦,也许通过下面你会对所谓vuejs和react框架的优势有更本质的认识。在官方的《对比其他框架》中和react比较时,其中有一条

React 和 Vue 都使用 Virtual DOM

有了虚拟DOM(Virtual DOM),就有真实的DOM,真实DOM是页面渲染完成的DOM树,它直接展示给用户。那么虚拟DOM又是什么技术?它有什么用?它在页面那里?看下某篇网方中对虚拟DOM和真实DOM的区别描述:

  1. 虚拟DOM不会进行排版与重绘操作
  2. 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
  3. 真实DOM频繁排版与重绘的效率是相当低的
  4. 虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部(同2)

看完之后,高大上的虚拟DOM就不再那么深奥了,还记得原生JS的文档片断DocumentFragment吗?虚拟DOM和它类似,所以 虚拟DOM是存在于内存中,主要作用就是在内存中完成数据运算,然后一起性渲染到真实DOM,减少真实DOM的渲染消耗,提高用户体验 。没深入理解过原生JS还真可能被这些框架绕糊涂,对于这些框架不要有什么神秘,它运用还是原生JS的知识,放心使用就可以,它逃不出原生JS的范畴,框架只是放大某些优点,方便用户高效开发而已。

2、数据data属性

数据data属性是vue的核心,它是操作数据的框架,无论后面讲的计算属性、侦听还是方法,都要操作数据。我就不按老师的思路来介绍了,我归纳成3个方面来介绍:脚本中如何访问data脚本中如何访问数组或对象元素如何使用data

脚本中如何访问data: 访问data可分为Vue外或Vue内的两种访问情况,但语法基本相同,前者使用vue实例对象,如常见的vm;后者因为在Vue内,所以一般使用this。

  • 语法:vm.\$data.name或vm.name(this.\$data.name或this.name)

vm.$data.name 是正常访问方式,那么vm.name为什么也能访问呢,正常的原生JS对象默认是不支持跨过访问的,Vue支持是因为它使用了 “数据代理访问” 技术,下面是用原生JS实现的数据访问代理:

  1. const user={
  2. $info:{
  3. name:'woxiaoyao',
  4. email:'14588823@qq.com',
  5. }
  6. }
  7. console.log('$info.nane => ',user.$info.name);
  8. // 直接跨中间访问是失败的
  9. console.log('nane => ',user.name);
  10. Object.defineProperty(user,'name',{
  11. // 访问器属性
  12. get: () => user.$info.name,
  13. // 设置器
  14. set: (value) => (user.$info.name = value),
  15. });
  16. console.log('nane => ',user.name);

通过Object.defineProperty可为属性重新定义get和set方法,替换默认的行为,就完成了拓展增强,这也是juqery函数库或vuejs框架等对原生JS的封装所在,将经常用的封装成方便的属性或方法,便于用户调用。

脚本中如何访问数组或对象: 为什么要说这个问题,上面已经讲过了数据访问代理,直接访问不就OK了吗?如果你是这样访问vuejs的数据的话,那么将会经常给你报错,尤其是数组和对象。下面是我的测试代码,看看在vuejs中字符型、数组和对象是什么类型。

  1. const vm = new Vue({
  2. el: '.container',
  3. data: {
  4. hello:'Hello VueJS.cn',
  5. hobby:['计算机','摄影','电影'],
  6. info:{name:'woxiaoyao',age:18},
  7. },
  8. mounted:function(){
  9. console.log('hello => ',this.hello);
  10. console.log('hobby => ',this.hobby);
  11. console.log('info => ',this.info);
  12. }
  13. });

data

对于字符型访问没什么问题,但是数组和对象则不可以,如上图中它有个Observer字样,虽然最后proto还是数组和对象,但它不再是原来的数组或对象了。至于Observer后面再探讨,这里先解决怎么访问数组和对象。使用 原生JS的两个方法:JSON.parse和JSON.stringify 。如上面访问数组可以是: let hobby=JSON.parse(JSON.stringify(this.hobby)) 就可以转为原生JS的普通数组了,也就是我们代码中样式了。

元素如何使用data: MVVM最大的优点就是数据绑定,Vuejs也实现了,它可以数据变化实时反映到视图中,那么元素使用data?

  • 第一种是 模板语法类似于原生JS的innerText 。它的 本质就是表达式,表达式中每项是普通JS数据,每项都可以使用原生JS的数据属性或方法 。如老师演示时案例
    <p>{{hello.split('').reverse().join('').substr(0,2)+'=> hobby:'+hobby.toString()}}</p>
  • 第二种是 v-html属性类似于原生JS的innerHTML ,支持解析带有元素标签的数据。如vm.data.text="Hello <strong style='color:red'>php.cn</strong>"。此时用模板语法则所有都当成普通文本来显示了,想解析元素标签则需要v-html指令属性。如
    <p v-html="text"></p>就可以了。

3、计算属性computed

计算属性computed在 编译时最终和与data中的属性变量合并 ,计算属性默认只有 getter,不过在需要时你也可以提供一个 setter。下面是官方的计算属性完整范例

  1. computed: {
  2. fullName: {
  3. // getter
  4. get: function () { return this.firstName + ' ' + this.lastName },
  5. // setter
  6. set: function (newValue) {
  7. var names = newValue.split(' ')
  8. this.firstName = names[0]
  9. this.lastName = names[names.length - 1]
  10. }
  11. }
  12. }

这个范例也说明了在使用计算属性时,一定要用 属性名来访问 ,它会默认调用getter方法。此时有个问题就是 如何给计算属性的getter传递参数? 就如我的购物车用vuejs案例中,单项商品总价是计算属性,它依赖单项商品的数量。直接使用给计算属性的get方法传递参数是报错的。我的 解决方案是它的返回值就是函数 ,这样就可以 支持传递参数了 。如下面:
get: function () { return function(index){return '第'+index+'个:'+this.firstName + ' ' + this.lastName} },

题外话:原生JS的事件监听器的回调函数默认只支持事件对象参数,若要传递其它参数则需要将返回值为函数才支持,实现原理同上。具体应用见《购物车三种实现(原生JS、jquery和vuejs)》https://www.php.cn/blog/detail/24847.html

那么计算属性和数据属性中直接使用方法有什么区别? 下面演示了同样计算的计算属性和数据属性中方法。实际使用中注意事项:

  • 计算属性使用只要 属性名 ,后面()不要,而数据属性中则是 属性名()
  • 计算属性和数据属性名不能相同 ,否则属性名返回的是数据属性中方法源码,而不是计算属性。数据属性比计算属性优先级高,也就是数据属性和计算属性相同的属性名时,计算属性无效。
  • 计算属性虽然是属性,但本质是getter和setter的结合,默认是get方法,返回数据,所有使用时不用(),但定义时不能少()。
  1. const vm = new Vue({
  2. el: '.container',
  3. data: {
  4. hello: 'Hello VueJS.cn',
  5. // reverHello(){
  6. // return this.hello.split('').reverse().join('');
  7. // }
  8. },
  9. computed:{
  10. reverHello() { return this.hello.split('').reverse().join(''); },
  11. },
  12. });

4、过滤器filters

计算属性最终合并到数据属性中,而过滤器则是根据需要定义了数据操作的方法,Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方: 双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道|”符号指示

  1. <div class="container">
  2. <p>计算属性:{{reverHello | wordsToCase}}</p>
  3. </div>
  4. <script>
  5. const vm = new Vue({
  6. el: '.container',
  7. data: {
  8. hello: 'Hello VueJS.cn',
  9. },
  10. computed:{
  11. reverHello() { return this.hello.split('').reverse().join(''); },
  12. },
  13. filters:{
  14. wordsToCase(str){ return str.toUpperCase(); }
  15. }
  16. });
  17. </script>

过滤器可以串联:{{ message | filterA | filterB }} 过滤器函数总是接收表达式的值 (之前的操作链的结果) 作为第一个参数,依次计算,最后过滤器返回结果。filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。

过滤器可以接受参数:{{ message | filterA(arg1, arg2) }} 这里要来演示一下官方的解释,不然很难懂。下面已经说明了在 Vue中定义是filterA(str,arg1, arg2),之前的操作链的结果是第一个参数,从第二个参数开始对应传递的参数

  1. <div class="container">
  2. <p>过滤器:{{hello | wordsToCase('start','end') }}</p>
  3. </div>
  4. <script>
  5. const vm = new Vue({
  6. el: '.container',
  7. data: {
  8. hello: 'Hello VueJS.cn',
  9. },
  10. filters:{
  11. wordsToCase(str,arg1,arg2){ return arg1+' '+str.toUpperCase()+' '+arg2.toLowerCase(); }
  12. }
  13. });
  14. </script>

5、侦听器watch

官方解释完全从应用角度,而老师却说出了本质,这样应用才不会迷茫,老师在解释侦听器时说它是事件监听器,它负责监视数据变化时需要执行的操作,类似于change事件,这样就不会再难分什么时候用计算属性,什么时候用侦听器了,按官方解释,你就会被绕晕了。

  1. <div class="container">
  2. <input type="number" v-model:value="add1" name="" id="" /> + <input type="number" v-model:value="add2" name="" id="" /> =<span>{{res}}</span>
  3. </div>
  4. <script>
  5. const vm = new Vue({
  6. el: '.container',
  7. data: {
  8. add1:0,
  9. add2:0,
  10. res:0,
  11. },
  12. watch:{
  13. add1:function(newVal, oldVal){
  14. console.log(newVal);
  15. this.res=newVal*1+this.add2*1;
  16. },
  17. add2:function(newVal, oldVal){
  18. console.log(newVal);
  19. this.res=newVal*1+this.add1*1;
  20. }
  21. }
  22. });
  23. </script>

上面是演示代码,在使用watch要注意两个方面:

  1. 必须要用v-model指令绑定的变量才支持watch 上面绑定若是普通的:value=”add1”则无法被侦听,它是单向的,即脚本改变会影响视图中数据,但反过来则不行。要想双向影响则要使用v-model指令。

  2. 侦听器的 语法:函数名(新值newVal,旧值oldVal)

四、vuejs入门实战案例:简单计算器

功能描述:

  1. 对两个数进行加、减、乘和除运算
  2. 可先输入数字再选择运算符,也可先选择运算符再输入数,均可正常实时运算
  3. 切换为除法时,若除数为0则弹窗提示错误,并不切换为除法
  4. 输入数字计算时,若除法时除数为0,则结果处提示错误。

实现介绍:

  • 第1项 通过侦听器watch侦听两个运算数,若变化则根据运算符进行运算
  • 切换运算符 通过原生JS事件委托代理,将事件触发者的自定义数据属性event.target.dataset.index记录到vue的data属性中,便于运算时判断是加、减、乘和除运算
  • 第2项 前面是通过侦听器实现,后者是通过按钮点击事件实现
  • 第3项 除法时除数为0,采用原生JS的alert弹窗提示错误
  • 第4项 通过过滤器filters,当除法时除数为0则计算值为Infinity,此时可赋值为错误提示。
  1. <style>
  2. * {
  3. margin: 0;
  4. padding: 0;
  5. outline: none;
  6. border: none;
  7. box-sizing: border-box;
  8. }
  9. .container {
  10. width: 30em;
  11. height: 15em;
  12. padding: 0.5em;
  13. color: white;
  14. background-color: #007d20;
  15. margin: 2em auto;
  16. }
  17. .container h2 {
  18. text-align: center;
  19. }
  20. .container .box {
  21. padding: 1em;
  22. display: grid;
  23. grid-template-columns: 12em 1fr;
  24. grid-template-rows: repeat(4, 2em);
  25. gap: 1em 0.5em;
  26. }
  27. .container .btns {
  28. grid-column: span 2;
  29. display: flex;
  30. justify-content: space-around;
  31. }
  32. .container .btns button {
  33. width: 5em;
  34. border-radius: 0.5em;
  35. }
  36. .container .btns button.selected {
  37. color: white;
  38. background-color: red;
  39. }
  40. .container p {
  41. grid-column: span 2;
  42. color: yellow;
  43. }
  44. .container p span {
  45. color: white;
  46. font-size: 1.2em;
  47. margin-left: 2em;
  48. }
  49. </style>
  50. <div class="container">
  51. <h2>简单计算器</h2>
  52. <div class="box">
  53. <label for="add1">第一个数(10万以内的数):</label>
  54. <input type="number" name="add1" id="add1" v-model="add1" min="0" max="100000" placeholder="10万以内的数" />
  55. <label for="add2">第二个数(10万以内的数):</label>
  56. <input type="number" name="add2" id="add2" v-model="add2" min="0" max="100000" placeholder="10万以内的数"/>
  57. <div class="btns" @click="btnClick">
  58. <button data-index="0" :class="{'selected':index==0}"></button>
  59. <button data-index="1" :class="{'selected':index==1}"></button>
  60. <button data-index="2" :class="{'selected':index==2}"></button>
  61. <button data-index="3" :class="{'selected':index==3}"></button>
  62. </div>
  63. <p>
  64. 结果:
  65. <span>{{res | checkDiv(index,add2)}}</span>
  66. </p>
  67. </div>
  68. </div>
  69. <script>
  70. const vm = new Vue({
  71. el: '.container',
  72. data: {
  73. add1: 0,
  74. add2: 0,
  75. res: 0,
  76. index: 0,
  77. },
  78. watch: {
  79. add1(newVal, oldVal) {
  80. // console.log('add => ',this);
  81. switch (this.index * 1) {
  82. case 1:
  83. this.res = newVal * 1 - this.add2 * 1;
  84. break;
  85. case 2:
  86. this.res = newVal * 1 * this.add2 * 1;
  87. break;
  88. case 3:
  89. // if (this.add2 == 0) return false;
  90. this.res = ((newVal * 1) / this.add2) * 1;
  91. break;
  92. default:
  93. this.res = newVal * 1 + this.add2 * 1;
  94. }
  95. },
  96. add2(newVal, oldVal) {
  97. switch (this.index * 1) {
  98. case 1:
  99. this.res = this.add1 * 1 - newVal * 1;
  100. break;
  101. case 2:
  102. this.res = this.add1 * 1 * newVal * 1;
  103. break;
  104. case 3:
  105. // if (this.add2 == 0) return false;
  106. this.res = ((this.add1 * 1) / newVal) * 1;
  107. break;
  108. default:
  109. this.res = this.add1 * 1 + newVal * 1;
  110. }
  111. },
  112. },
  113. filters: {
  114. checkDiv: function (str, index, add2) {
  115. // console.log(this);
  116. // console.log(index,add2);
  117. // if (this.index*1 == 0 && this.add2*1 == 0) str = '除法时,除数不能为0';
  118. return str == 'Infinity' ? '除法时,除数不能为0' : str;
  119. },
  120. },
  121. methods: {
  122. // 事件代理委托来处理所有按钮点击事件
  123. btnClick: function () {
  124. // console.log(event.target.dataset.index);
  125. // console.log(event.currentTarget);
  126. if (event.target.dataset.index == 3 && this.add2 == 0) {
  127. alert('除法时,除数不能为0');
  128. return false;
  129. }
  130. this.index = event.target.dataset.index;
  131. switch (this.index * 1) {
  132. case 1:
  133. this.res = this.add1 * 1 - this.add2 * 1;
  134. break;
  135. case 2:
  136. this.res = this.add1 * 1 * this.add2 * 1;
  137. break;
  138. case 3:
  139. if (this.add2 == 0) return false;
  140. this.res = ((this.add1 * 1) / this.add2) * 1;
  141. break;
  142. default:
  143. this.res = this.add1 * 1 + this.add2 * 1;
  144. }
  145. },
  146. },
  147. });
  148. </script>

calculator

Codepen演示 https://codepen.io/woxiaoyao81/pen/GRqeZdM

一点意外的发现: 本来过滤器提示错误信息是通过index=3和add2=0来判断的,但是在 过滤器中直接使用this是访问不到vue实例的!!! 这点和计算属性、侦听器、自定义方法是不相同的。网上有 两种解决方案

  • 定义全局变量,如let that,然后在beforeCreate中将vue实例赋值给它,如that=this 。在vue实例中可以使用that访问vue的data数据。这点和微信小程序非常类似。
  • 在视图中使用过滤器时将 vue实例的data作为过滤器参数传入 。经测试是可行的。至于过滤器的参数对应关系在本文第三部分有介绍,不清楚可以再看下。

五、学习后总结

在原生JS基础上再学习vuejs框架不仅理解更深、混合运用也是灵活多变,这样才是技术使用最佳状态,终于体会到老师在群里说的,框架和函数库不用规定什么时候使用,应该是想什么时候使用就什么时候使用,为了发挥各自的优点而灵活的混合使用 。两个字:舒服。

Correcting teacher:天蓬老师天蓬老师

Correction status:qualified

Teacher's comments:利用已有知识, 可以尝试用原生代码实现几个vue中的指令,试试看
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
1 comments
吾逍遥 2020-11-17 16:51:29
这个想法可以,可以加深对原生JS和vuejs的理解,老师可否提供几个思路
1 floor
Author's latest blog post