模板编译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来表示。

最外层结束后,里面还有一层, imgimg 可以视其为跟 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-ifv-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 被当作函数的参数进行传递,并列返回对应 itemli 列表元素。

(10)编译事件

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

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

事件

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

对于事件来说,首先会创建一个 button 元素,之后 @clickv-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 的背后,绑定的是 namevalue 这两个语法糖。之后通过 attrs 去创建 类型typetext 的属性。

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

3、模板编译总结

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

(1)从render函数到vnode

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

(2)vnode到patch和diff

基于 vnode 的基础上,再执行 patchdiff

(3)模板编译工具

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

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

1、初识组件渲染/更新

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

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

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

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

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

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

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

2、组件渲染/更新过程

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

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

(1)初次渲染过程

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

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

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

1)解析模板为render函数

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

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

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

2)触发响应式

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

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

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

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

3)执行render函数,生成vnode

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

(2)更新过程

1)更新过程细述

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

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

触发完 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.mzph.cn/news/307983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;又或者看了某些综艺节目后想玩一玩…