文章目录

  • 1. 模板引擎的介绍
    • 1.1 模板引擎是什么?
    • 1.2 模板引擎是怎么来的?(发展历史)
      • 1. 使用原生的DOM操作
      • 2. 使用数组中的join方法
      • 3. 使用ES6反引号的方法
  • 2. mustache基本使用
    • 2.1 mustache 库的简介
      • 引入 mustache 库
      • mustache 的模板语法
      • 1. 最简单的情况——不循环对象数组
      • 2. 循环最简单的数组
      • 3. 循环对象数组 (v-for类似)
      • 4. 循环嵌套对象数组和简单数组
      • 5. 控制元素的显示与隐藏——布尔值
      • 6. `script` 模板写法
  • 3. mustache的原理
    • 3.1 replace()方法 和 正则表达式实现最简单的模板数据填充
      • 预备知识
        • replace()方法
        • 正则的捕获
      • 实现
    • 3.2 mustache 的实现原理
    • 3.3 什么是 tokens?
      • 1. 最简单的形式
      • 2. 循环数组情况下的 tokens
      • 3. 多重循环
    • 3.4 实现 mustache 库 的重点是
  • 4. 手写实现mustache库
    • 4.1 配置webpack环境
    • 4.2 实现 Scanner 扫描器类
      • 预备知识 JS中字符串提取字串的方法
      • src/Scanner.js
      • www/index.html
      • src/index.js
    • 4.3 生成 tokens 数组
      • 4.3.1 完成简单的一层数组
        • src/parseTemplateToTokens
        • www/index.html
        • src/index.js
      • 4.3.2 完成嵌套数组(重难点)
        • src/nestTokens.js
    • 4.4 将tokens解析为DOM字符串
      • index.html
      • src/index.js
      • src/lookup.js 可以在对象中,寻找连续点符号的属性
      • 简化版 src/lookup.js
      • src/renderTemplate.js
      • src/parseArray.js 递归调用 renderTemplate
    • 4.5 完善空格问题

来自尚硅谷的课程笔记 课程链接[尚硅谷邵山欢(考拉老师)Vue之mustache模板引擎]
加入大量的注释以及改写

1. 模板引擎的介绍

1.1 模板引擎是什么?

模板引擎是将数据data变为视图view(html)的解决方案

数据:

视图:

Vue的解决方案

<li v-for="item in arr"></li>

1.2 模板引擎是怎么来的?(发展历史)

1. 使用原生的DOM操作

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>01_数据变为视图-纯DOM法</title>
</head><body><ul id="list"></ul><script>var arr = [{ name: '小明', age: 12, sex: '男' },{ name: '小红', age: 11, sex: '女' },{ name: '小强', age: 13, sex: '男' },]var list = document.getElementById('list')for (let i = 0; i < arr.length; i++) {// 每遍历一项,都要用 DOM 方法去创建 li 标签let oLi = document.createElement('li')// 创建 hd 这个 divlet hdDiv = document.createElement('div')hdDiv.className = 'hd'hdDiv.innerText = arr[i].name + '的基本信息'oLi.appendChild(hdDiv)// 创建 bd 这个 divlet bdDiv = document.createElement('div')bdDiv.className = 'bd'oLi.appendChild(bdDiv)// 创建 3 个 p 标签let p1 = document.createElement('p')p1.innerText = '姓名:' + arr[i].namebdDiv.appendChild(p1)let p2 = document.createElement('p')p2.innerText = '年龄:' + arr[i].agebdDiv.appendChild(p2)let p3 = document.createElement('p')p3.innerText = '性别:' + arr[i].sexbdDiv.appendChild(p3)// 创建的节点是孤儿节点,所以必须要上树才能让用户看见list.appendChild(oLi)}</script></body></html>

2. 使用数组中的join方法

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>02_数据变为视图-数组join法</title>
</head><body><ul id="list"></ul><script>var arr = [{ name: '小明', age: 12, sex: '男' },{ name: '小红', age: 11, sex: '女' },{ name: '小强', age: 13, sex: '男' },]var list = document.getElementById('list')// 遍历 arr 数组,每遍历一项,就以字符串的视角将HTML字符串添加到list中for (let i = 0; i < arr.length; i++) {list.innerHTML += ['<li>','  <div class="hd">' + arr[i].name + '的信息</div>','  <div class="bd">','    <p>姓名:' + arr[i].name + '</p>','    <p>年龄:' + arr[i].age + '</p>','    <p>性别:' + arr[i].sex + '</p>','  </div>','</li>'].join('')}</script></body></html>

3. 使用ES6反引号的方法

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>03_数据变为视图-ES6反引号法</title>
</head><body><ul id="list"></ul><script>var arr = [{ name: '小明', age: 12, sex: '男' },{ name: '小红', age: 11, sex: '女' },{ name: '小强', age: 13, sex: '男' },]var list = document.getElementById('list')// 遍历 arr 数组,每遍历一项,就以字符串的视角将HTML字符串添加到list中for (let i = 0; i < arr.length; i++) {list.innerHTML += `<li><div class="hd">${arr[i].name}的基本信息</div><div class="bd"><p>姓名:${arr[i].name}</p><p>年龄:${arr[i].age}</p><p>性别:${arr[i].sex}</p></div></li>`}</script></body></html>

2. mustache基本使用

2.1 mustache 库的简介

  • mustache 官方 git:https://github.com/janl/mustache.js
  • mustache 中文翻译是 “胡子”
  • mustache 是最早的模板引擎库,非常有创造性的、轰动性的

引入 mustache 库

<script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.1.0/mustache.js"></script>

mustache 的模板语法

<ul>
{{#arr}}<li><div class="hd">{{name}}的基本信息</div><div class="bd"><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p></div></li>
{{/arr}}
</ul>

1. 最简单的情况——不循环对象数组

  <div id="container"></div><h1></h1><script>var templateStr = `<h1>我买了一个{{thing}},好{{mood}}啊</h1>`var data = {thing: '华为手机',mood: '开心'}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

2. 循环最简单的数组

  <div id="container"></div><h1></h1><script>var templateStr = `<ul>{{#arr}}<li>{{.}}</li>{{/arr}}  </ul>`var data = {arr: ['苹果', '梨子', '香蕉']}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

3. 循环对象数组 (v-for类似)

  <div id="container"></div><script>var templateStr = `<ul id="list">{{#arr}}<li><div class="hd">{{name}}的基本信息</div><div class="bd"><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p></div></li>{{/arr}}</ul>`var data = {arr: [{ name: '小明', age: 12, sex: '男' },{ name: '小红', age: 11, sex: '女' },{ name: '小强', age: 13, sex: '男' },]}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

4. 循环嵌套对象数组和简单数组

  <div id="container"></div><h1></h1><script>var templateStr = `<ul>{{#arr}}<li>{{name}}的爱好是:<ol>{{#hobbies}}<li>{{.}}</li>{{/hobbies}}</ol></li>{{/arr}}  </ul>`var data = {arr: [{ name: '小明', age: 12, hobbies: ['游泳', '羽毛球'] },{ name: '小红', age: 11, hobbies: ['编程', '写作文', '看报纸'] },{ name: '小强', age: 13, hobbies: ['打台球'] }]}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

5. 控制元素的显示与隐藏——布尔值

true显示
false不显示

  <div id="container"></div><h1></h1><script>var templateStr = `{{#m}}<h1>哈哈哈</h1>{{/m}}`var data = {m: true}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

6. script 模板写法

scirpt标签中写入模板,只要type的值不是text/javascript,都不会被当作js执行解析,这样可以在script标签中写入模板,可以高亮可以自动填充

  <div id="container"></div><!-- 模板 --><script type="text/template" id="mytemplate"><ul id="list">{{#arr}}<li><div class="hd">{{name}}的基本信息</div><div class="bd"><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p></div></li>{{/arr}}</ul></script><script>var templateStr = document.getElementById('mytemplate').innerHTMLvar data = {arr: [{ name: '小明', age: 12, sex: '男' },{ name: '小红', age: 11, sex: '女' },{ name: '小强', age: 13, sex: '男' },]}var domStr = Mustache.render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr</script>

3. mustache的原理

3.1 replace()方法 和 正则表达式实现最简单的模板数据填充

预备知识

replace()方法

这个方法接收两个参数,第一个参数可以是一个RegExp对象或一个字符串(这个字符串不会转换为正则表达式),第二个参数可以是一个字符串或一个函数

replace()的第二个参数可以是一个函数。在只有一个匹配项时,这个函数会收到3个参数:与整个模式匹配的字符串、匹配项在字符串中的开始位置,以及整个字符串

console.log('我爱踢足球,我爱脱口秀'.replace(/我/g, function (a, b, c) {console.log(a, b, c)return '你'
}))

正则的捕获

/\}\}{(\w+)\}\}/

表示捕获{{}}中间的多个文字或数字

var templateStr = '<h1>我买了一个{{thing}},好{{mood}}的啊</h1>'
console.log(templateStr.replace(/\{\{(\w+)\}\}/g, function (a, b, c, d) {console.log(a, b, c, d)return '*'
}))

实现

<div id="container"></div><script>var templateStr = '<h1>我买了一个{{thing}},花了{{money}},好{{mood}}</h1>'var data = {thing: '华为手机',money: 5999,mood: '开心'}// 最简单的模板引擎实现机理,利用的是正则表达式中的 replace() 方法// replace() 的第二个参数可以是一个函数,这个函数提供捕获的东西的参数,就是 $1 结合data对象,即可进行智能的替换// function中的参数分别是:①findStr匹配到的部分{{thing}} ②捕获到的thing ③位置9 ④原串function render(templateStr, data) {return templateStr.replace(/\{\{(\w+)\}\}/g, function (findStr, $1) {return data[$1]})}var domStr = render(templateStr, data)var container = document.getElementById('container')container.innerHTML = domStr
</script>

3.2 mustache 的实现原理

3.3 什么是 tokens?

tokens 是JS的嵌套数组,就是模板字符串的JS表示
它是抽象语法树虚拟节点等的开山鼻祖

1. 最简单的形式

模板字符串

<h1>我买了一个{{thing}},好{{mood}}啊</h1>

tokens

[["text", "<h1>我买了一个"],["name", "thing"],["text", "好"],["name", "mood"],["text", "啊</h1>"]
]

2. 循环数组情况下的 tokens

3. 多重循环

3.4 实现 mustache 库 的重点是

  1. 模板字符串编译为 tokens
  2. tokens 结合数据data,解析为 DOM 字符串

4. 手写实现mustache库

4.1 配置webpack环境

使用 webpack 和 webpack-dev-server 构建

新建目录 YK_TemplateEngine

cd YK_TemplateEngine
npm init -yes
cnpm install -D webpack@4 webpack-cli@3 webpack-dev-server@3

新建 webpack.config.js 文件 设置代码如下

const path = require("path");module.exports = {mode: "development",// 入口entry: "./src/index.js",// 出口output: {filename: "bundle.js",},// 配置webpack-dev-serverdevServer: {// 静态根目录contentBase: path.join(__dirname, "www"),// 不压缩compress: false,// 端口号port: 8080,// 虚拟打包的路径,bundle.js文件没有真正生成publicPath: "/xuni/",},
};

新建 src/index.js

alert('nihao')

新建 www/index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><h1>我是index.html</h1><script src="xuni/bundle.js"></script>
</body></html>

package.json 文件中新增命令:

{"scripts": {"dev": "webpack-dev-server",}
}

终端运行 npm run dev
访问:http://localhost:8080/http://127.0.0.1:8080/xuni/bundle.js, 可以看到 www/index.htmlxuni/bundle.js 文件的内容

4.2 实现 Scanner 扫描器类

预备知识 JS中字符串提取字串的方法

ECMAScript提供了3个从字符串中提取子字符串的方法:slice()substr()substring()
这3个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数。
第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置
slice()substring()而言,第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)。
substr()而言,第二个参数表示返回的子字符串数量
任何情况下,省略第二个参数都意味着提取到字符串末尾。
concat()方法一样,slice()substr()substring()不会修改调用它们的字符串,而只会返回提取到的原始新字符串值

src/Scanner.js

/*** 扫描器类*/
export default class Scanner {constructor(tempalteStr) {this.tempalteStr = tempalteStr;// 指针this.pos = 0;// 尾字符串,一开始是模板字符串原文this.tail = tempalteStr;}// 扫描走过指定内容{{或}},没有返回值scan(tag) {if (this.tail.indexOf(tag) === 0) {// tag 有多长,比如“{{”长度是2,就让指针向后移动多少位this.pos += tag.length;this.tail = this.tempalteStr.substr(this.pos);}}// 让指针进行扫描,直到遇到指定{{或}}内容结束,返回结束之前路过的文字scanUtil(stopTag) {// 记录开始执行时 pos 的值const post_backup = this.pos;// 当尾字符串的开头不是stopTag时,说明还没有扫描到stopTag && 寻找到最后找不到while (!this.eos() && this.tail.indexOf(stopTag) !== 0) {this.pos++;// 改变尾字符串 从当前指针到最后的全部字符this.tail = this.tempalteStr.substr(this.pos);}return this.tempalteStr.substring(post_backup, this.pos);}// 判断指针是否已经到头 end of string 到头了eos() {return this.pos >= this.tempalteStr.length}
}

www/index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><h1>你好!!!</h1><script src="xuni/bundle.js"></script><script>var templateStr = '<h1>我买了一个{{thing}},好{{mood}}啊</h1>'var data = {thing: '华为手机',mood: '开心'}SGG_TemplateEngine.render(templateStr, data)</script>
</body></html>

src/index.js

import Scanner from "./Scanner";// 全局提供YK_TemplateEngine对象
window.YK_TemplateEngine = {// 渲染方法render(tempalteStr, data) {// 实例化一个扫描器,构造时提供一个参数,参数就是模板字符串// 也就是这个扫描器就是针对这个模板字符串工作的var scanner = new Scanner(tempalteStr);var word// pos指针没有到头while (!scanner.eos()) {word = scanner.scanUtil("{{");console.log(word+'***');scanner.scan("{{");word = scanner.scanUtil("}}");console.log(word);scanner.scan("}}");}},
};

4.3 生成 tokens 数组

4.3.1 完成简单的一层数组

src/parseTemplateToTokens

import Scanner from "./Scanner";
/*** 将模板字符串转换成tokens数组*/
export default function parseTemplateToTokens(tempalteStr) {var tokens = [];// 创建扫描器var scanner = new Scanner(tempalteStr);var words// 让扫描器工作while (!scanner.eos()) {// 收集开始标记出现之前的文字words = scanner.scanUtil("{{");if (words !== '') {tokens.push(['text', words])}scanner.scan("{{");// 收集words = scanner.scanUtil("}}");if (words !== '') {// 这是{{}} 中间的东西,判断首字符if (words[0] === '#') {tokens.push(['#', words.substring(1)])} else if (words[0] === '/') {tokens.push(['/', words.substring(1)])} else {tokens.push(['name', words])}}scanner.scan("}}");}return tokens;
}

www/index.html

// 模板字符串
// var tempalteStr = '<h1>我买了一个{{thing}},好{{mood}}啊</h1>'
var tempalteStr = `
<div><ol>{{#students}}<li>学生{{name}}的爱好是<ol>{{#hobbies}}<li>{{.}}</li>{{/hobbies}}</ol></li>{{/students}}</ol></div>`
// 数据
var data = {thing: 'phone',mood: 'happy'
}
YK_TemplateEngine.render(tempalteStr, data)

src/index.js

import parseTemplateToTokens from './parseTemplateToTokens'// 全局提供YK_TemplateEngine对象
window.YK_TemplateEngine = {// 渲染方法render(tempalteStr, data) {// 调用parseTemplateToTokens,可以让模板字符串变为tokens数组var tokens = parseTemplateToTokens(tempalteStr)console.log(tokens)},
};

4.3.2 完成嵌套数组(重难点)


来解决
遇见#就进栈,遇见/就出栈

src/nestTokens.js

折叠tokens,将#xxx和/xxx之间的tokens能够折叠成Array(n),作为xxx的末尾数组 ["#", “xxx”, Array(n)]

/*** 折叠tokens,将#xxx和/xxx之间的tokens能够折叠成Array(n),作为xxx的末尾数组 ["#", "xxx", Array(n)]* @param {*} tokens*/
export default function nestTokens(tokens) {// 结果数组,存储最后的嵌套数组var nestedTokens = [];// 栈,存放 # / 之间的tokens,栈顶的tokens数组中是当前操作的// 遇到 # 时,入栈 将#xxx和/xxx之间的tokens能够折叠成Array(n),["#", "xxx", Array(n)]// 遇到 / 时,出栈var sections = [];// 收集器数组,为 栈顶 或 结果数组 收集tokens// 初始指向 nestedTokens结果数组,引用类型值,所以指向的是同一个数组// 入栈后,改变指向:入栈后栈顶末尾数组 token[2]// 出栈后,根据栈是否为空改变指向: 出栈后栈顶末尾数组 sections[sections.length - 1][2] 或 结果数组nestedTokensvar collector = nestedTokens;for (let token of tokens) {// 判断token的第0个元素是什么switch (token[0]) {case "#":// 收集器中放入这个token(初始是nestedTokens数组,当栈中有元素时,指向栈顶token末尾数组)collector.push(token);// 入栈sections.push(token);// 改变收集器指向,指向给token添加下标为2的项collector = token[2] = [];break;case "/":// 出栈sections.pop();// 栈不空的情况下 改变收集器为 sections栈顶 末尾数组// 栈空就直接指向结果数组collector =sections.length > 0 ? sections[sections.length - 1][2] : nestedTokens;break;// 普通的tokendefault:// 栈中有元素,就进入栈顶末尾数组;栈中没有元素,就进入结果数组collector.push(token);}}return nestedTokens;
}

4.4 将tokens解析为DOM字符串

index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><h1>这是index.html</h1><div id="container"></div><script src="xuni/bundle.js"></script><script>// 模板字符串// var tempalteStr = '<h1>我买了一个{{thing}},好{{mood}}啊</h1>'var tempalteStr = `<div><ul>{{#students}}<li class="ii">学生{{name}}的爱好是<ol>{{#hobbies}}<li>{{.}}</li>{{/hobbies}}</ol></li>{{/students}}</ul></div>`// 数据var data = {students: [{name: '小红',hobbies: ['羽毛球', '跆拳道']},{name: '小明',hobbies: ['足球']},{name: '小王',hobbies: ['魔术', '学习', '游戏']}]}let domStr = YK_TemplateEngine.render(tempalteStr, data)console.log(domStr)let container = document.getElementById('container');container.innerHTML = domStr;</script>
</body></html>

src/index.js

import parseTemplateToTokens from "./parseTemplateToTokens";
import renderTemplate from "./renderTemplate";// 全局提供YK_TemplateEngine对象
window.YK_TemplateEngine = {// 渲染方法render(tempalteStr, data) {// 调用parseTemplateToTokens,可以让模板字符串变为tokens数组var tokens = parseTemplateToTokens(tempalteStr);var domStr = renderTemplate(tokens, data);return domStr},
};

src/lookup.js 可以在对象中,寻找连续点符号的属性

/*** 可以在dataObj对象中,寻找连续点符号的keyName属性 比如a.b.c  {a:{b:{c:100}}}* @param {object} dataObj* @param {string} keyName*/
export default function lookup(dataObj, keyName) {// 判断keyName中有没有点符号,但不能是.本身if (keyName.indexOf(".") !== -1 && keyName !== '.') {let temp = dataObj; // 临时变量用于周转,一层一层找下去let keys = keyName.split(".");for (let key of keys) {temp = temp[key];}return temp;}return dataObj[keyName]
}

简化版 src/lookup.js

export default function lookup(dataObj, keyName) {// 只有一个元素不影响最终结果,不影响循环语句最终结果,所以不用判断keyName中有没有点符号return keyName !== '.' ? keyName.split('.').reduce((prevValue, currentKey) => prevValue[currentKey], dataObj) : dataObj[keyName]
}

src/renderTemplate.js

import lookup from './lookup'
import parseArray from './parseArray'
/*** 让 tokens数组 变成 DOM字符串* @param {array} tokens* @param {object} data*/
export default function renderTemplate(tokens, data) {// 结果字符串let resultStr = "";for (let token of tokens) {if (token[0] === "text") {resultStr += token[1];} else if (token[0] === "name") {resultStr += lookup(data, token[1]);} else if (token[0] === "#") {// 递归调用 renderTemplateresultStr += parseArray(token, data)}}return resultStr;
}

src/parseArray.js 递归调用 renderTemplate

import lookup from "./lookup";
import renderTemplate from "./renderTemplate";
/*** 处理数组,结合renderTemplate实现递归* 参数时token不是tokens 是一个简单的数组 ['#', 'arr', Array[n]]* * 递归调用renderTemplate函数,调用次数由data决定* 比如data是这样的* {students: [{name: '小红',hobbies: ['羽毛球', '跆拳道']},{name: '小明',hobbies: ['足球']},{name: '小王',hobbies: ['魔术', '学习', '游戏']}]}* parseArray()函数要递归调用renderTemplate函数3次,数组的长度=3  */
export default function parseArray(token, data) {// console.log(token, data);// 得到data中这个数组需要使用的部分let newData = lookup(data, token[1]);// console.log(newData);// 结果字符串let resultStr = '';for (let item of newData) {resultStr += renderTemplate(token[2], {// 展开newData[i] 并加入 点 数组...item,'.': item})}return resultStr;
}

4.5 完善空格问题

  1. 普通文字的空格直接去掉
  2. 标签中的空格不能去掉,比如 <div class="box"><></div> 不能去掉class前面的空格
// 收集开始标记之前的文字
words = scanner.scanUtil('{{')
// 存起来
if (words !== '') {// 判断普通文字的空格,还是标签中的空格// 标签中的空格不能去掉,比如 <div class="box"><></div> 不能去掉class前面的空格let isInJJH = false// 空白字符串var _words = ''for (let i = 0; i < words.length; i++) {// 判断是否在标签里if (words[i] === '<') {isInJJH = true} else if (words[i] === '>') {isInJJH = false}if (!/\s/.test(words[i])) {_words += words[i]} else {// 如果这项是空格,只有当它在标签内的时候,才拼接上if (isInJJH) {_words += words[i]}}}tokens.push(['text', _words])
}

【Vue源码】mustache模板引擎 - 基本使用 - 底层原理 - 手写实现相关推荐

  1. VUE源码:模板引擎mustache

    文章目录 模板引擎的定义 mustache的基本使用 手写原理代码(简化版) 模板引擎的定义 模板引擎就是将数据变为视图最优雅的解决方案 例如:VUE的v-for.mustache 历史上数据变为视图 ...

  2. [Vue源码分析] 模板的编译

    最近小组有个关于vue源码分析的分享会,提前准备一下- 前言: Vue有两个版本:Runtime + Compiler . Runtime only ,前者是包含编译代码的版本,后者不包含编译代码,编 ...

  3. pytorch实现手写数字识别_送源码!人工智能实现:识别图片中的手写数字,值得收藏...

    作者|小林同学 关注<高手杰瑞>,每天有不一样的实用小教程发布哦! 哈喽,大家好我是杰瑞.今天我给大家带来一个用机器学习的方法来实现手写数字识别的教程,就像C语言中输出的那一行" ...

  4. 【Spring源码】Spring中的AOP底层原理分析

    AOP中的几个概念 Advisor 和 Advice Advice,我们通常都会把他翻译为通知,其实很不好理解,其实他还有另外一个意思,就是"建议",我觉得把Advice理解为&q ...

  5. VUE源码学习第一篇--前言

    一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...

  6. Vue源码之mustache模板引擎(二) 手写实现mustache

    Vue源码之mustache模板引擎(二) 手写实现mustache mustache.js 个人练习结果仓库(持续更新):Vue源码解析 webpack配置 可以参考之前的笔记Webpack笔记 安 ...

  7. Vue源码之mustache模板引擎(一)

    Vue源码之mustache模板引擎(一) 个人练习结果仓库(持续更新):Vue源码解析 抽空把之前学的东西写成笔记. 学习视频链接:[尚硅谷]Vue源码解析之mustache模板引擎 模板引擎是什么 ...

  8. 学习Vue的mustache语法-mustache模板引擎

    学习地址 : https://www.bilibili.com/video/BV1EV411h79m?vd_source=a81826692f4afea80764f4048dc1ae0a 代码地址 : ...

  9. Vue 合同模板_vue源码逐行注释分析+40多m的vue源码程序流程图思维导图

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...

最新文章

  1. java构造方法可以重载吗_Java基础教程之构造器与方法重载
  2. [鸟哥的Linux私房菜] X Window 与 纯文本模式的切换
  3. 从零开始学python电子书-从零开始学Python程序设计 PDF
  4. 关于引用与指针实现多态的一些记录
  5. C++类与const关键字
  6. 框架:Spring Aop、拦截器、过滤器的区别
  7. grldr 和 grldr.mbr 区别
  8. JAVA8 Stream方法使用详解Filter、map等用法(一)
  9. 不会Nacos的配置中心?你怎么敢的呀!如何一步步实现Nacos作为服务的配置中心(全网最详系列)
  10. AspectJ基于xml和基于注解
  11. linux vim命令跳到67行,Linux学习之Vim/Vi使用(十三)
  12. 使用即时通讯(IM)SDK心得体验
  13. 使用物理硬盘_许迎果 第147期 机械硬盘的物理结构和逻辑结构
  14. linux 上安装 Node.js和npm
  15. pcb布线注意点:晶振
  16. html实现2048小游戏,html+css+js适合前端小白的实战全解(超详细)——2048小游戏(二)...
  17. adb 驱动 fastboot 驱动 win10
  18. (三) SiamRPN从论文角度介绍
  19. python---之struck.pack()和struct.unpack
  20. python在一个函数中调用另一函数中的变量

热门文章

  1. 柑橘病虫害识别方案总结
  2. win7适合安哪个版本的python?(赠送Python永久使用安装包)
  3. 跨境电商亚马逊跟卖的跟卖具体步骤是怎样的
  4. c++操作符重载(数组类重载[]、=、==、!=操作符)
  5. java httpunit_实例简介HttpUnit结合JUnit自动测试Web应用
  6. 才从Guava中明白了一件事:不可变特性与防御性编程
  7. 实现js程序在vscode控制台输出的方法(看完方便多了)
  8. Sort Colors
  9. Kubernetes CRD (CustomResourceDefinition) 自定义资源类型
  10. web服务器可以上传信息吗,Web服务器的架设上传.doc