文章目录

  • 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. 【模板】树状数组 2
  2. 计算机保研去北科大还是大工,全部保研!大工这寝室太牛
  3. 安装linux18双系统,Win10 安装Linux ubuntu-18.04双系统(安装指南)
  4. Arcmap格式转arcgis的shp格式
  5. 开发文档怎么编写_PoC 编写指南
  6. 蓝昭餐饮管理系统服务器无法连接,服务器安全加固操作指南.docx
  7. 5元以下纯铜小摆件_【拍4斤发5斤】早餐饼干网红早餐代餐曲奇酥性小饼干零食500g6元优惠券券后价5.8元...
  8. python第三方库文件传输_本地 Python 代码上传到 Python 第三方库(Pypi)
  9. 通过深度优先搜索(DFS)对图的边进行分类
  10. redis tutorail
  11. 让 Chrome 在后台运行
  12. JavaMail概述
  13. SAP Hybris企业培训
  14. C++重温笔记(五): 多态与虚函数
  15. gitbook 入门教程之从零到壹发布自己的插件
  16. 资深ios开发工程师收藏的iOS干货文章、大神的blog博客
  17. 微信公众号 测试号 申请
  18. 会计三张主要报表(资产负债表、利润表和现金流量表)
  19. Java中的switch
  20. 利用幂级数性质解级数求和问题

热门文章

  1. 解决宏基acer电脑开机出现start pxe over ipv4 press esc exit
  2. 使用xshell登录阿里云
  3. react项目开发中出现浏览器翻译功能造成的bug
  4. 打开Excel的报错,提示:不能使用对象链接和嵌入
  5. 英语阅读速度飞升只需加粗几个字母,网友试后直呼快得停不下来,华为NLP专家:这很合理...
  6. 在网页上通过JS实现文本的语音朗读
  7. 敏捷软件开发中的风险管理
  8. 专访美图 CTO 张伟:5年以前,我们也不会想到会花 70% 的时间在人工智能上
  9. 以太坊客户端Ethereum Wallet与Geth区别简介
  10. Simple_Wallet 在服务器端通过RPC操纵钱包