模板编译template的背后,究竟发生了什么事?带你了解template的纸短情长

template cover

解析模板编译template的背后发生了什么

  • 一、📑初识模板编译
    • 1、vue组件中使用render代替template
    • 2、模板编译总结
  • 二、✏️感受模板编译的美
    • 1、with语法
      • (1)例子展示🌰
      • (2)知识点归纳
  • 三、📈编译模板
    • 1、编译模板碎碎念
    • 2、编译模板过程
      • (1)初始化一个npm环境
      • (2)安装编译器
      • (3)新建新文件
      • (4)了解缩写函数
      • (5)编译插值
      • (6)编译表达式
      • (7)编译属性和动态属性
      • (8)编译条件
      • (9)编译循环
      • (10)编译事件
      • (11)编译v-model
    • 3、模板编译总结
  • 四、🔑组件渲染/更新过程
    • 1、初识组件渲染/更新
    • 2、组件渲染/更新过程
      • (1)初次渲染过程
        • 1)解析模板为render函数
        • 2)触发响应式
        • 3)执行render函数,生成vnode
      • (2)更新过程
        • 1)更新过程细述
        • 2)完成流程图
      • (3)异步渲染
    • 3、小结
  • 五、✔️结束语

依稀记得我们在vue时,最上方总是有一个 template 包围着。而很多时候,我们也没有很在意的去意识到 <template></template> 究竟是什么。

在今天的这篇文章中,就带大家一起来了解,模板编译 template 的背后,究竟发生了什么事情?

一起来了解模板编译的纸短情长🚋🚋🚋

一、📑初识模板编译

1、vue组件中使用render代替template

template ,即模板。模板是 vue 开发中最常用的部分,即与vue的使用关联最紧密的原理。它不是 html ,它有指令、有插值、也有 JS 表达式,那它,到底是什么呢?我们来看个例子。

在 vue 中定义一个组件,通常会使用 template 模板字符串来定义一个组件。比如:

Vue.component('heading',{template:`xxx`
})

一般情况下,模板的定义是上面这种情况。同时,在程序编译期间,模板会将 template 的这种字符串类型,编译成 render 函数。


但是呢,在有些复杂的情况下,可能就不能用 template 函数了,这个时候会考虑直接用 render 函数来定义一个组件。比如:

Vue.component('heading',{render: function(createElement){return createElement('h' + this.level, //tag props[  //childrencreateElement('a',{attrs:{name:'headerId',href:'#' + 'headerId'}},'this is a tag')])}
})

就像上面这样子,我们也可以通过使用一个 render 函数来定义一个组件。

2、模板编译总结

看完上面的例子,我们来做个小结✨

  • template,即模板。这个模板会编译成 render 函数,其中 render 函数用的是 with 语法。
  • 过程:模板→ render 函数→ vnode →组件渲染和更新过程。
  • vue 组件可以用 render 函数代替 template 。
  • React 一直都用 render ,没有模板(这里仅作知识补充,不做讲解)。

二、✏️感受模板编译的美

1、with语法

(1)例子展示🌰

先来了解模板编译中一个很重要的知识点, with 语法。下面先用一个例子来展示with语法与普通语法的不同。

不使用with语法执行程序时:

const obj = {a: 100, b: 200}console.log(obj.a)
console.log(obj.b)
console.log(obj.c) //undefined

使用with语法执行程序时:

//使用with,能改变 {} 内自由变量的查找方式
// 将 {} 内自由变量,当作 obj 的属性来查找
with(obj){console.log(a)console.log(b)console.log(c) //会报错!!!
}

(2)知识点归纳

看完上面with语法的例子,我们来对 with 语法做一个知识点归纳。

  • with 语法会改变 {} 内自由变量的查找规则,当作 obj 属性来查找;
  • 如果在当前 {} 内找不到匹配的 obj 属性,就会报错;
  • with 要谨慎使用,它打破了作用域规则,会让其易读性变差。

三、📈编译模板

1、编译模板碎碎念

在前面中我们讲过,模板它不是 html ,它有指令、有插值、也有JS表达式,它能实现判断、也能实现循环。

试想一下模板为什么不是 html ?

思考一下,假如你在写程序时,能用 html 写出一个判断或者循环出来吗?答案自然时不行的。

所以说, html 只是一个静态的标签语言,你写什么它就显示什么,它没有办法实现一个逻辑,或者做循环和判断。

因此,对于前端浏览器而言,只有 JS 才能实现判断和循环等各种逻辑功能。

所以,模板一定是转换为某种 JS 代码之后才进行运行的。而这个模板怎么转换成 js 代码的这个过程,就称为编译模板。

那这个模板是怎么转的呢?接下来我们来看下编译模板的过程。

2、编译模板过程

(1)初始化一个npm环境

首先先建立一个新文件,可以命名为 vue-template-complier-demo 。之后用以下命令行初始化一个npm的环境:

npm init -y

(2)安装编译器

npm 安装模板编译器。命令行如下:

npm install vue-template-compiler --save

(3)新建新文件

在根目录下初始化新建一个 index.js 文件,并引入 vue-template-compiler 。代码如下:

//引入vue-template-compiler
const compiler = require('vue-template-compiler')// 编译
const res = compiler.compile(template)
console.log(res.render)

接下来我们就来看下,模板中的插值、表达式、属性和动态属性等等类型的编译,到底是怎么样的?

(4)了解缩写函数

以下vue源码中的缩写函数先了解,将在下面的讲解中用到。

// 从 vue 源码中找到缩写函数的含义
function installRenderHelpers (target) {target._c = createElement;target._o = markOnce;target._n = toNumber;target._s = toString;target._l = renderList;target._t = renderSlot;target._q = looseEqual;target._i = looseIndexOf;target._m = renderStatic;target._f = resolveFilter;target._k = checkKeyCodes;target._b = bindObjectProps;target._v = createTextVNode;target._e = createEmptyVNode;target._u = resolveScopedSlots;target._g = bindObjectListeners;target._d = bindDynamicKeys;target._p = prependModifier;
}

(5)编译插值

//引入vue-template-compiler
const compiler = require('vue-template-compiler')// 插值
const template = `<p>{{message}}</p>`
// with(this){return createElement('p',[createTextVNode(toString(message))])}
// h -> vnode
// createElement -> vnode// 编译
const res = compiler.compile(template)
console.log(res.render)

编译以上内容,打印结果如下:

编译插值

从上图中可以看到,插值类型的模板最终被编译成一个 with 语句,并且这个 with 语句的参数都指向了 this 。

同时,大家可以看到,里面有一个 _c , _v , _s。那这几个元素是什么呢?

这个就是上面第四点中提到的 vue 源码中的缩写函数。 _c 对应的就是源码中的 createElement, _v 对应的就是源码中的 createTextVNode ,_s 对应的就是源码中的 toString 。

所以,以上编译后的 with 语句 with(this){return _c('p',[_v(_s(message))])} ,事实上就是 with(this){return createElement('p',[createTextVNode(toString(message))])} 。

以上这个语句的意思为,编译创建一个 p 元素,之后呢, p 元素就没有子元素了,于是就创建它的文本节点 message ,同时 message 是字符串的形式存在,因此要进行 toString 。

额外再补充一个知识点, createElement 其实就等于我们平常所说的 h 函数,返回的是一个 虚拟DOM 节点。

以上就是一个插值模板编译的过程,下面再用几个例子让大家熟悉。

(6)编译表达式

//引入vue-template-compiler
const compiler = require('vue-template-compiler')// 表达式
const template = `<p>{{flag ? message : 'no message found'}}</p>`
// with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])}// 编译
const res = compiler.compile(template)
console.log(res.render)

编译以上内容,打印结果如下:

表达式

依据上面插值的分析方法,我们来分析表达式的模板编译过程。

表达式编译后的结果返回了一个虚拟 DOM 节点,同样地,查询 vue 源码中的缩写函数我们可以发现, with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])} 最终的结果等于 with(this){return createElement('p',[createTextVnode(toString(flag ? message : 'no message found'))])} 。

先创建了一个 p 元素,之后 p 元素没有子元素了,于是创建文本节点,最终 toString 三目表达式里面的内容。

(7)编译属性和动态属性

//引入vue-template-compiler
const compiler = require('vue-template-compiler')// 属性和动态属性
const template = `<div id="div1" class="container"><img :src="imgUrl"/></div>
`
// with(this){return _c('div',
//      {staticClass:"container",attrs:{"id":"div1"}},
//      [
//          _c('img',{attrs:{"src":imgUrl}})])}// 编译
const res = compiler.compile(template)
console.log(res.render)

编译以上内容,打印结果如下:

属性和动态属性

依据上面的分析方法,我们来分析属性和动态属性的模板编译过程。

属性和动态属性编译后的结果返回了一个虚拟 DOM 节点,同样地,查询 vue 源码中的缩写函数我们可以发现, with(this){return _c('div',{staticClass:"container",attrs:{"id":"div1"}},[_c('img',{attrs:{"src":imgUrl}})])} 最终的结果等于 with(this){return createElement('div',{staticClass:"container",attrs:{"id":"div1"}},[createElement('img',{attrs:{"src":imgUrl}})])} 。

此时我们可以看到,返回的 vnode 节点中,包含 class 名字, container 。此时 div 有一个 id 选择器,这个 id 选择器是该 div 的一个属性,于是就通过attrs来表示。

最外层结束后,里面还有一层, img 。 img 可以视其为跟 div 一样的标签,于是先创建 img 元素,又因为 img 绑定了一个具体的值,就像是 div 里面绑定了 id 选择器。所以在创建完 img 的值之后,继续用 attrs 来传递 img 所绑定的值。

(8)编译条件

// 条件
const template = `<div><p v-if="flag === 'a'">A</p><p v-else>B</p></div>
`
// with(this){return _c('div',[(flag === 'a')?_c('p',[_v("A")]):_c('p',[_v("B")])])}

编译以上内容,打印结果如下:

条件

依据上面的分析方法,我们来分析条件的模板编译过程。

对于条件来说,首先是先创建一个 div 元素,之后呢,模板编译把 v-if 和 v-else 分割成一个三目表达式的方式来进行编译。

(9)编译循环

// 循环
const template = `<ul><li v-for="item in list" :key="item.id">{{item.title}}</li></ul>
`
// with(this){return _c('ul',_l((list),function(item){return _c('li',{key:item.id},[_v(_s(item.title))])}),0)}

编译以上内容,打印结果如下:

循环

依据上面的分析方法,我们来分析循环的模板编译过程。

对于以上循环来说,首先会创建一个 ul 元素,之后查询 _l 的缩写函数我们知道它是 renderlist , 所以 list 列表会被 renderList 函数进行编译。

最后渲染后的 item 被当作函数的参数进行传递,并列返回对应 item 的 li 列表元素。

(10)编译事件

// 事件
const template = `<button @click="clickHandler">submit</button>
`
// with(this){return _c('button',{on:{"click":clickHandler}},[_v("submit")])}

编译以上内容,打印结果如下:

事件

依据上面的分析方法,我们来分析事件的模板编译过程。

对于事件来说,首先会创建一个 button 元素,之后 @click 即 v-on:click 会被编译成 on:{"click":clickHandler} 。最后是 _v ,即 createTextVNode 。创建一个 submit 的文本节点,将 click 的内容提交上去。

(11)编译v-model

// v-model
const template = `<input type="text" v-model="name">`
// 主要看 input 事件
// with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(name),expression:"name"}],attrs:{"type":"text"},domProps:{"value":(name)},on:{"input":function($event){if($event.target.composing)return;name=$event.target.value}}})}

编译以上内容,打印结果如下:

v-model

依据上面的分析方法,我们来分析双向绑定v-model的模板编译过程。

对于 v-model 来说,主要看的是 input 事件。 v-model 的背后,绑定的是 name 和 value 这两个语法糖。之后通过 attrs 去创建 类型type 为 text 的属性。

最终是 input 事件, input 事件绑定 $event ,最后, name 的值就等同于 $event.target.value ,这样,数据就实现了双向绑定。

3、模板编译总结

看完上述的内容,我们来对模板编译做个小结:

(1)从render函数到vnode

模板编译后是一个 render 函数,执行 render 函数后返回一个 vnode ;

(2)vnode到patch和diff

基于 vnode 的基础上,再执行 patch 和 diff ;

(3)模板编译工具

在平常的开发中,我们可以使用 webpack 、 vue-loader 等构建工具,在开发环境下编译模板。

四、🔑组件渲染/更新过程

1、初识组件渲染/更新

讲完上完的内容,我们再来讲一个与编译模板关联性很强的知识点:组件渲染/更新过程。

一个组件,从渲染到页面上开始,再到修改 data 去触发更新(数据驱动视图),其背后的原理是什么,又需要掌握哪些要点呢?

事实上,组件在渲染之前,会先进行模板编译,模板 template 会编译成 render 函数。

之后就是数据的监听了,这就要谈到响应式数据。vue的响应式通过操作 Object.defineProperty() ,去监听 getter 和 setter 方法,来使得数据实时更新。

监听完数据之后,就是执行 render 函数,生成 vnode 。

到了 vnode (即 vdom )这一步之后,会进行 patch(elem,vnode) 和 patch(vnode,newVnode) 的比较。

关于响应式原理和vdom的解读,如有需要可以查看我的前两篇文章进行学习,这里不再展开细述~

2、组件渲染/更新过程

组件渲染和更新过程主要经过以下三个步骤:初次渲染过程→更新过程→异步渲染。

接下来就这三个步骤进行一一讲解。

(1)初次渲染过程

初次渲染过程,即组件第一次渲染是怎么样的,怎么把模板渲染到页面上。具体有以下三个步骤:

  • 解析模板为 render 函数;
  • 触发响应式,监听 data 属性 getter 和 setter ;
  • 执行 render 函数,生成 vnode ,进行 patch(elem,vnode) 。

下面就这三个步骤来进行一一讲解。

1)解析模板为render函数

在开发环境下,解析模板为 render 函数一般是由 vue-loader 这个插件来处理的。还有一种情况就是,用户直接用 cdn 的方式引入 vuejs 的文件进行本地代码练习,这种情况下,解析模板为 render 函数就是在浏览器环境运行的。

小知识了解完,我们来看下这个步骤。

解析模板为 render 函数,即解析 template 为 render 函数,这个就是上述文章中说的编译模板。

2)触发响应式

在编译完模板之后, render 函数有了,我们来开始监听 data 属性。

监听 data 属性,这个时候我们就需要触发响应式,也就是渲染数据。

那在这个阶段怎么渲染数据呢?

这个阶段我们需要执行 render 函数, render 函数会触发 getter 方法,因为数据没有进行更新,只是进行渲染。只有在进行渲染的时候才会操作 setter 方法。

3)执行render函数,生成vnode

最后,当数据渲染完毕后,就会执行第一步生成的 render 函数,然后生成虚拟 DOM 节点 vnode ,之后进行 patch(elem,vnode) 。

(2)更新过程

1)更新过程细述

更新过程,即 data 修改之后,组件是怎么更新的。

在这个阶段呢,将会修改 data ,并且触发 setter (注意:在此之前 data 在 getter 中已经被监听)。

触发完 setter 之后,重新执行 render 函数,并生成 newVnode ,最后进行 patch(vnode, newVnode) 的diff比较。

2)完成流程图

接下来我们用一张流程图来完整的回顾渲染和更新的过程。

模板编译流程图

(3)异步渲染

在渲染和更新结束之后,我们的程序可能还有可能会发生多个程序同时加载,这就涉及到一个异步渲染问题。

异步渲染问题,我们用 $nextTick 来作为例子讲解。

假设我们现在要实现一个功能,当我们点击按钮时,打印出列表的项数。这个时候我们大多人可能会这么操作。

<template><div id="app"><!-- ref的设置时为了方便后续可以用来:取节点的DOM元素 --><ul ref="ul1"><li v-for="(item, index) in list" :key="index">{{item}}</li></ul><button @click="addItem">添加一项</button></div>
</template><script>
export default {name: 'app',data() {return {list: ['a', 'b', 'c']}},methods: {addItem() {this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)// 获取 DOM 元素const ulElem = this.$refs.ul1console.log( ulElem.childNodes.length )}}
}
</script>

此时浏览器的显示效果如下:

nextTick

细心的小伙伴已经发现,浏览器并没有按照我们所想的打印。当页面上的列表显示 6项 内容时,此时控制台只打印 3项 ;当显示 9项 时,此时控制台直接只打印 6项 。

那这究竟时为什么呢?

其实,当我们点击的那一刻, data 发生变化,但是 DOM 并不会立刻进行渲染。所以等到我们点击完成的时候,获取的元素还是原来触发的内容,而不会增添上新的内容。

那我们所期望的是,当点击之后立刻触发 DOM 渲染并拿到最新的值。这个时候就需要用到 nextTick 。具体代码如下:

<script>
export default {name: 'app',data() {return {list: ['a', 'b', 'c']}},methods: {addItem() {this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调,//    即NextTick函数会在多次data修改完并且全部DOM渲染完再触发,仅在最后触发一次// 2. 页面渲染时会将 data 的修改做整合this.$nextTick(() => {// 获取 DOM 元素const ulElem = this.$refs.ul1console.log( ulElem.childNodes.length )})}}
}
</script>

我们通过给获取 DOM 元素的代码外面再嵌套一层 $nextTick 函数,来达到我们想要的效果。在此过程中,当我们点击结束后, data 的值发生变化,此时 $nextTick 会等待DOM全部渲染完成之后再进行回调。

最终浏览器的打印效果如下:

nextTick2

所以,也就是说, $nextTick 通过汇总 data 的修改,最后再一次性更新视图。

这样可以减少 DOM 的操作次数,大大的提高了性能。

3、小结

经过上述一系列的讲解,我们可以把内容分割成以下两个要点:

  • 要理解清楚渲染和响应式、渲染和模板编译、渲染和vdom的关系。
  • 要理解组件渲染/更新的过程:初次渲染过程→更新过程→异步渲染。

五、✔️结束语

从模板编译,到组件渲染更新过程,我们了解了整个 template 背后的全过程。相信通过本文的学习,大家对模板编译有了一个更深的认识。

关于模板编译的内容就讲到这里啦!如有不理解或文章有误,欢迎评论区留言或私信我交流~

  • 关注公众号 星期一研究室 ,不定期分享学习干货,更多有趣的专栏待你解锁~
  • 如果这篇文章对你有帮助,记得 点个赞加个关注 再走哦~
  • 我们下期见!🥂🥂🥂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/307983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode24. 两两交换链表中的节点(思路+解析)

一:题目 二:思路 思路: 1.分析题意 这是相邻结点进行交换 如果是4个结点 那么1和2交换 3和4交换 如果是3个结点 那么就1和2进行交换 3不动 2.这里我们定义一个虚拟头节点方便操作&#xff0c;我们只需三步实现结点的交换 <1>:让虚拟结点指向第二个结点(进行交换的结点我…

把Autofac玩的和java Spring一样6

大家好&#xff0c;今天来介绍我开源的一个autofac.Annotation项目 源码&#xff1a;https://github.com/yuzd/Autofac.Annotation本项目是autofa的一个扩展组件&#xff0c;autofac是一个老牌的DI容器框架 &#xff0c;支持netframework和netcoreAnnotdation是注解的意思&…

『软件测试5』测开岗只要求会黑白盒测试?NO!还要学会性能测试!

浅谈软件测试中的性能测试一、&#x1f92a;性能测试概念1、为什么要有性能测试&#xff1f;2、性能测试是什么&#xff1f;3、性能测试的目的二、&#x1f910;性能测试指标1、响应时间2、吞吐量3、并发用户数4、TPS(Transaction Per Second)5、点击率6、资源利用率三、&#…

CLR的简单理解

CLR加载程序生成进程&#xff0c;一个进程中可以存在多个线程&#xff0c;当创建一个线程时&#xff0c;会分配1Mb的空间&#xff0c;也就是线程的栈空间&#xff0c;对应jvm的虚拟机堆栈&#xff0c;是线程执行过程中用到的工作内存。这片内存用于方法传递实参&#xff0c;并存…

『软件测试6』bug一两是小事,但安全漏洞是大事!

详解软件测试中的安全测试一、&#x1f4bf;安全测试概念1、安全测试概述2、安全测试与软件生命周期的关系3、常规测试与安全测试的不同&#xff08;1&#xff09;测试目标不同&#xff08;2&#xff09;假设条件不同&#xff08;3&#xff09;思考域不同&#xff08;4&#xf…

我们真的需要JWT吗?

JWT&#xff08;JSON Web Token&#xff09;是目前最流行的认证方案之一。博客园、各种技术公众号隔三差五就会推一篇JWT相关的文章&#xff0c;真的多如牛毛。但我对JWT有点困惑&#xff0c;今天写出来跟大家探讨探讨&#xff0c;不要喷哈。JWT原理本文默认读者已经对JWT有所了…

leetcode面试题 02.07. 链表相交

一:题目 二:思路 1.这道题我们是需要找到一个结点&#xff0c;并且从这个结点往后的结点都相等 2.我们需要将两个链表 右对齐 3.然后将长链表的指针移动到和短链表头结点相同的位置 4.接下来就是比较指针&#xff0c;当一个指针相同也就意味着往后的结点的数值也相等 三:上码…

详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务

队列在前端中的应用一、队列是什么二、应用场景三、前端与队列&#xff1a;事件循环与任务队列1、event loop2、JS如何执行3、event loop过程4、 DOM 事件和 event loop5、event loop 总结四、宏任务和微任务1、引例2、宏任务和微任务&#xff08;1&#xff09;常用的宏任务和微…

终于弄明白了 Singleton,Transient,Scoped 的作用域是如何实现的

一&#xff1a;背景1. 讲故事前几天有位朋友让我有时间分析一下 aspnetcore 中为什么向 ServiceCollection 中注入的 Class 可以做到 Singleton&#xff0c;Transient&#xff0c;Scoped&#xff0c;挺有意思&#xff0c;这篇就来聊一聊这一话题&#xff0c;自从 core 中有了 S…

leetcode142. 环形链表 II(暴力+双链表)

一:题目 二:思路 1.双指针 快慢指针(快指针一次一个结点&#xff0c;慢指针一次两个结点) 2.如果有环的话&#xff0c;那么快慢指针肯定会相遇 3.那么相遇的地点一定在环中 因为如果没有环的话慢指针是永远追不到快指针的 4.接下来就是判断出口在那里&#xff0c;我们定义一个…

动态 Restful API 生成

介绍通常在DDD开发架构中&#xff0c;我们写完服务层需要在控制器中写API&#xff0c;今天介绍一个组件 Plus.AutoApi 可以用它来动态生成 Restful 风格的 WebApi&#xff0c;不用写 Controller。快速使用在你的应用服务层中添加组件Install-Package Plus.AutoApi在 Startup 中…

卷死了!再不学vue3就没有人要你了!速来围观vue3新特性

一文全面了解vue3新特性一、&#x1f636;vue3比vue2有什么优势&#xff1f;二、&#x1f9d0;Vue3升级了哪些重要的功能1、createApp2、emits(父子组件间的通信)&#xff08;1&#xff09;通信方式&#xff08;2&#xff09;举个例子&#x1f330;3、多事件处理4、Fragment5、…

idea报错Class not found (在target中没有生成对应的class文件)

一&#xff1a;问题描述 二:解决 既然他不自动生成&#xff0c;那么我们就手动导入&#xff1b; 点击后应用 然后再次运行我们的测试用例&#xff1b;如果不行 再取消勾选 然后再运行我们的测试用例

敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs

一文了解Composition API新特性&#xff1a;ref、toRef、toRefs一、&#x1f64e;如何理解ref、toRef和toRefs1、ref、toRef和toRefs是什么&#xff08;1&#xff09;ref1&#xff09;ref是什么2&#xff09;举个例子&#x1f330;&#xff08;2&#xff09;toRef是什么1&#…

C# 枚举转列表

C# 枚举转列表独立观察员 2020 年 9 月 1 日今天有朋友问我&#xff0c;ComboBox 怎么绑定一个 Enum&#xff0c;其实他的意思是枚举如何转换为列表。想想这确实是一个挺正常的需求&#xff0c;但我一时也只想到遍历&#xff0c;他觉得麻烦&#xff0c;于是我在网上帮忙查了一下…

leetcode242. 有效的字母异位词(两种方法map或数组)

一:题目 二:上码 1:方法一&#xff08;map解法&#xff09; class Solution { public:bool isAnagram(string s, string t) {/**思路:1.分析题意&#xff0c;这个是要判断t中的字符出现次数和s中字符出现的次数相同2.可以用map<char,int>来做*/map<char,int>m1,m…

活久见!月薪30k的小程序全栈开发到底有多难?

10年前&#xff0c;公司的标配是门户网站&#xff0c;造就了一批网站工作室。随着移动互联网大潮兴起&#xff0c;App又成了企业标配&#xff0c;IOS和Android开发赚的盆满钵满。然而App导致的手机内存告急&#xff0c;无止尽的信息推送&#xff0c;让微信小程序应运而生。然而…

卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!

vue3进阶新特性一、&#x1f4d7;watch和watchEffect1、watch和watchEffect的区别2、举个例子&#xff08;1&#xff09;wtach监听&#xff08;2&#xff09;watchEffect监听二、&#x1f4d8;setup如何获取组件实例&#xff08;1&#xff09;为什么需要获取组件实例&#xff0…

leetcode349. 两个数组的交集(思路+详解)

一:题目 二:上码 class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {/**思路:本题我们采用的哈希表数据结构是unordered_set,没有用数组因为我们不确定给出的数组中数值的范围如果 强上会出现空间的大量…

高效掌握新技能的「树型思维」

大家好&#xff0c;我是Z哥。不知道你有没有过这样的困惑&#xff0c;想学习某项新技能&#xff0c;但是很容失败。比如&#xff0c;出于职业发展的考虑&#xff0c;想学习一门新的编程语言&#xff0c;或者想了解一个新的技术框架&#xff1b;又或者看了某些综艺节目后想玩一玩…