vue API 知识点(1)---全局 API 总结

张宁187 2020-11-08 16:13:39
vue API 知识 知识点 全局


1.Vue.extend(options)

构造器,创建一个 子类 。参数是一个包含组件选项的对象

data 选项是特例,需要注意 在 Vue.extend() 中它必须是一个函数,

 <div id="test"></div>
 // 创建构造器
let MyTest = Vue.extend({
template: '<p>{{name}} {{age}}</p>',
data(){
return {
name: 'zhangning',
age: '24'
}
}
})

创建 MyTest 实例,并挂载到一个元素上

 new MyTest().$mount('#test')

最终结果如下

 <p>zhangning 24</p>

上面 extend 创建的是 Vue 构造器,而不是一个具体的组件实例,所以不能够通过 new Vue({components: testExtend}) 来直接使用,需要通过 new MyTest().$mount('#test') 来挂载到指定的元素上

*调用 Vue.extend() 创建的是一个组件构造器

*通常在创建组件构造器时,传入 template 代表我们自定义组件的模板

*该模板就是在使用到组件的地方,要显示的 HTML 代码

*但是,这种写法在vue2之后几乎就不再使用了,都是直接使用语法糖,不过我们还是要学习的,为后面的学习打下基础

现在我们通常不使用 Vue.extend() 来注册组件了,使用语法糖的写法,直接把对象放在 Vue.component() 来注册组件,可以看第 7 个API进行学习

调用Vue.component() 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名字

所以需要传递两个参数:1.注册组件的标签名 2.组件构造器

组件必须挂在在某个Vue实例下,否则不会生效

示例:封装一个全局提示组件

首先创建一个 message.vue 组件

 <template>
<transition>
<div v-show='show'>{{message}}</div>
</transition>
</template>
<script>
export default{
data(){
return {
show: false,
message: ''
}
}
}
</script>
<style scoped>
div {
padding: 10px;
color: #ddd;
background: red;
text-align: center;
position: fixed;
top: 30%;
left: 50%;
} </style>

然后在 main.js 中配置

 import Message from './components/Message.vue'
const MessageM = {
install: function(Vue) {
// 创建一个 vue 的子类组件
const MessageConstructor = Vue.extend(Message)
// 创建子类实例,并挂载到动态创建的元素上,并将元素添加到全局结构中
const inst = new MessageConstructor().$mount(document.createElement('div'))
document.body.appendChild(inst.$el)
// 在 vue 原型链上注册方法,控制组件
Vue.prototype.$message = (msg, time = 1000) => {
inst.message = msg
inst.show = true
setTimeout((
inst.show = false
), time)
}
}
}
Vue.use(MessageM)

在组件内使用

 this.$message('消息提示')

以上就是一个简单的全局消息提示

 

2.Vue.nextTick

在写词DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM

<template>
<div>
<div ref='valRef'>{{ val }}</div>
<div>{{ val1 }}</div>
<div>{{ val2 }}</div>
<div>{{ val3 }}</div>
<el-button type="primary" @click.once='handleClick'>改变val</el-button>
</div>
</template>
<script>
export default {
name: 'index',
data() {
return {
val: 'zhangning',
val1: '',
val2: '',
val3: ''
}
},
methods: {
// 点击按钮时,val1和val3获取的是初次加载时候的val的值,因为vue的DOM是异步加载,
// 而使用了nextTick的val2的值在msg改变之后,就立刻获取到了val更新之后的值
 handleClick() {
this.val = '我已改变';
// DOM 未更新,获取不到最新的dom
this.val1 = this.$refs.valRef.innerHTML;
this.$nextTick(() => {
// DOM 已更新,获取最新的dom
this.val2 = this.$refs.valRef.innerHTML;
})
// DOM 未更新,获取不到最新的dom
this.val3 = this.refs.valRef.innerHTML;
}
}
}
</script>

官方给出的解释和例子(异步更新队列)

Vue 在更新 DOM 时是异步执行的。只要监听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然而在下一个的事件循环 tick 中,Vue 刷新队列并执行实际(已去重)工作。Vue 在内部对异步队列尝试使用原生的Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

例如,当设置 vm.someData = 'newVlaue',该组件不会立即重新渲染,当刷新队列时,组件会在下一个事件循环 tick 中更新。多数情况我们不需要关心这个过程,但是如果想基于更新后的 DOM 状态做点什么,这就有点棘手。虽然Vue鼓励我们使用数据驱动的方式思考,避免直接接触 DOM ,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成之后被调用。例如

<div id="example">{{message}}</div>
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})

在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue ,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上

Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
 })
}
}
})

因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的语法 async/await 完成相同的事情

methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
 }
}

 

3.Vue.set

Vue.set(target, key, value)

target:要更改的数据源(可以是对象或者数组)

key:要更改的具体数据

value:重新赋的值

由于 js 的限制,Vue 不能检测出数据的改变,但是通过这种方法修改数组中的数据,就会检测到数组的改变,是响应式的

用法:向响应式对象中添加一个 property, 并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 arr[1] = 110)

export default {
name: 'index',
data() {
return {
arr: [1,2,3]
}
},
methods: {
// 修改 arr[1] 变为 200
 handleChange(){
this.$set(this.arr, 1, 200);// arr: [1, 200, 3]
 },
// arr 数组添加元素 600
 handleAddArr(){
this.$set(this.arr, this.arr.length, 600);
}
}
}

 

4.Vue.delete()

Vue.delete(target, key, value)

target:要删除的数据源(可以是对象或者数组)

key:要删除的具体数据

用法:删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,虽然很少使用,但直到要会用

export default {
name: 'index',
data() {
return {
zhangning: {
height: 187,
name: 'zhangning',
sex: '男'
}
}
},
methods: {
// 删除 name
 handleDeleteName() {
this.$set(this.zhangning, 'name');
}
}
}

 

5.Vue.directive()

Vue.directive(string, Function | Object)

注册或获取全局指令

声明周期:

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定被插入文档中)

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生改变,也可能没有,但是你可以通过比较更新前后的值来忽略不必要的模板更新(详细的钩子函数参数见下)

    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用

unbind:只调用一次,指令与元素解绑时调用

钩子函数参数

el:指令所绑定的元素,可以用来直接操作 DOM

binding:一个对象,包含以下 property:

name:指令名,不包括 v- 前缀

value:指令的绑定值,例如:v-my-directive='1+1'中,绑定值为 2

oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用,无论值是否改变都可用

expression:字符串形式的指令表达式。例如 v-my-directive='1+1' 中,表达式为 ‘1+1’

arg:传给指令的参数,可选。例如 v-my-directive: foo 中,参数为 'foo'

modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 {foo: true, bar: true}

vnode:Vue 编译生成的虚拟节点

oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

 <div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
 Vue.directive('demo', {
bind: function (el, binding, vnode) {
 var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'expression: ' + s(binding.expression) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode).join(', ')
}
})
new Vue({
el: '#hook-arguments-example',
data: {
message: 'hello!'
}
})

页面展示效果如下

 name: 'demo'
value: 'hello!'
expression: 'message'
argument: 'foo'
modifiers: {a: true, b: true}
vnode keys: tag,data,children,text,elm,ns,context...

动态指令参数,指令的参数可以是动态的,例如,在 v-mydirective:[argument]='value'中,argument 参数可以根据组件数据进行更新,这可以让我们的自定义指令灵活使用。

创建一个自定义指令,把元素固定在距离顶部 200px 的位置

 <div id="test">
 <p>Scroll down the page</p>
 <p v-pin="200">距离顶部200px</p>
</div>
 Vue.directive('pin', {
bind: function(el, binding, vnode){
el.style.position = 'fixed'
el.style.top = binding.value + 'px'
}
})
new Vue({
el: '#test'
})

当我们需要固定在左侧而不是顶部的时候,这时候我们就需要用到动态指令

 <div id="test">
<p>Scroll down the page</p>
<p v-pin:[direction]="200">距离顶部200px</p>
</div>
 Vue.directive('pin', {
bind: function(el, binding, vnode){
el.style.position = 'fixed'
let s = binding.arg == 'left' ? 'left' : 'top'
el.style[s] = binding.value + 'px'
}
})
 new Vue({
el: '#test' ,
data(){
 return {
direction: 'left'
}
}
})

以上这个自定义指令现在的灵活性就足以支持一些不同的用例了

函数简写

很多时候,我们想在 bind 和 update 时触发相同行为,而不关心其他的钩子。

 Vue.directive('pin', function(el, binding){
el.style.top = binding.value + 'px'
})

如果指令需要多个值,可以传入 js 对象,指令函数能够接收所有合法的 js 表达式

 <div v-demo="{ color: 'red', text: 'hello'}"></div>
 Vue.directive('demo', function(el, binding){
console.log(binding.value.color) // red
console.log(binding.value.text) // hello
})

在项目中使用的案例

通过权限控制每个页面的按钮,

先定义一个 directive.js,写上全局指令 

import Vue from 'vue';
import store from './store';
Vue.directive('permission', {
inserted: (el, binding) => {
if (store.getters.userButtons.length) {
const buttons = store.getters.userButtons;
if (buttons.indexOf(binding.value) < 0) el.parentNode.removeChild(el);
} else {
store.dispatch('getInfo').then(data => {
const { buttons } = data.buttons;
if (buttons.indexOf(binding.value) < 0) el.parentNode.removeChild(el);
});
}
}
});

在 main.js 中全局引入

import './directive.js'

在 vue 组件中使用,判断有没有下载按钮权限

 <el-button
round
icon="el-icon-document"
type="primary"
v-permission="'SJML_SQXZ'"
@click="applyForDownload"
>下载文档</el-button>

 

6.Vue.filter()

定义过滤器,可被用于常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式,过滤器应该被添加在 js 表达式的尾部,由管道符号指示:'|'

 <!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div>

在一个组件的选项中定义本地过滤器

 filters: {
capitalize: function (value) {
 if (!value) return ''
value = value.toString()
 return value.charAt(0).toUpperCase() + value.slice(1)
}
}

在创建 Vue 实例之前全局定义过滤器

 Vue.filter('capitalize', function() {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})

注意:当全局过滤器和局部过滤器重名时,会采用局部过滤器

过滤器函数总接收表达式的值作为第一个参数

过滤器可以串联:

 {{ message | filterA | filterB }}

这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递给 filterB 中。

过滤器时 js 函数,因此可以接收参数

 {{ message | filterA('arg1', arg2) }}

这个例子,filterA 被定义为接收三个参数的过滤器函数,其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数, arg2 作为第三个参数

在项目中使用的案例

先定义一个 filters.js 

export { formatBoolean, formatDate, formatNum, formatStatus };
// 布尔值
function formatBoolean(value) {
return value ? "是" : "否";
}
// 状态
function formatStatus(value) {
return value ? "成功" : "失败";
}
// 时间戳转换
function formatDate(value) {
let date = new Date(parseInt(value));
let Y = date.getFullYear();
let M =
date.getMonth() + 1 < 10
? "0" + (date.getMonth() + 1)
: date.getMonth() + 1;
// let D = date.getDate();
let D = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
let h = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
let m = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
let s = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return Y + "." + M + "." + D + " " + h + ":" + m + ":" + s;
}
// 数值加逗号
function formatNum(num) {
num = (num || 0).toString();
let result = "";
while (num.length > 3) {
result = "," + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) {
result = num + result;
}
return result;
}

然后在 main.js 中进行全局引入

 import * as filters from './filters.js'
Object.keys(filters).forEach(item => {
Vue.filter(item, filters[item])
})
// 这里讲一下 Object.keys() 方法,表示给定对象的所有可枚举属性的字符串数组 let zn = {name: 'zhangning', age: '24', height: '187'} Object.keys(zn);// ['name', 'age', 'height'] 返回可枚举属性组成的数组 // 处理数组,返回索引值数组 let arr = [100, 200, 300, 400, 500] Object.keys(arr);// ['0', '1', '2', '3', '4'] 返回索引值字符串组成的数组 // 处理字符串,返回索引值数组 let str = 'zhang'; Object.keys(str);// ['0', '1', '2', '3', '4'] // 常用技巧 let zn = {name: 'zhangning', age: '25', address: '合肥', getName: function()} Object.keys(person).map(item => { person[item] // 获取到属性对应的值,做一些处理 })

 

7.Vue.component()

 // 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
 return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

data 必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果不是一个函数,就会影响到组件所有实例

在项目中自定义全局组件案例

首先创建一个组件 download.vue

然后创建一个 download.js,在 js 中引入组件

 // 引入组件
import DLFLoading from './index.vue';
// 自定义组件对象
const DownloadDialog = {
 // install 是默认的方法。当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数
install: function(Vue) {
 // Vue.component() 与正常的全局注册组件用法相同,可以理解为通过 install 和 Vue.use()函数注册了全局组件
Vue.component('DownloadDialog', DLFLoading);
}
}
// 导出
export default DownloadDialog;

接着在 main.js 中全局引入组件

 // 全局引入自定义下载 loading
import DownloadDialog from '@/components/DownloadDialog/download.js';
Vue.use(DownloadDialog);

最后在项目中直接就可以使用

 <DownloadDialog
@cancelDownload="cancelDownload"
:downloadOver="downloadOver"
:downloadLoading="downloadLoading"
></DownloadDialog>

 

8.Vue.use()

安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。

该方法需要在 new Vue() 之前被调用

当 install 方法被同一个插件多次调用,插件将只会被安装一次。

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制 一般有以下几种

1.添加全局方法或者property

2.添加全局资源:指令/过滤器/过渡等

3.通过全局混入来添加一些组件选项

4.添加 Vue 实例方法,通过把他们添加到 Vue.prototype 上实现

5.一个库,提供自己的 api,同时提供上面提到的一个或多个功能

使用插件

通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成

 // 调用 MyPlugin.install(Vue)
 Vue.use(MyPlugin)
new Vue({
// ...组件选项
})

 

也可以传入一个可选的选项对象

 Vue.use(MyPlugin, {someOption: true})

Vue.use 会自动阻止多次注册相同插件,即使多次调用也只会注册一次该插件

 

9.Vue.mixin()

全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为(官方不推荐使用)

官网全局混入案例。混入也可以进行全局注册,使用时格外小心,一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,它可以用来自定义选项注入处理逻辑。

// 为自定义的选项 myOption 注入一个处理器

 Vue.mixin({
created: function(){
var myOption = this.$options.myOption
if(myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'zhangning'
})
// => 'zhangning'

注意:要谨慎使用全局混入,因为它影响每个单独创建的 Vue 实例(包括第三方组件)。大多数情况下,之应当应用于自定义选项,就像上面实例一样,推荐将其作为插件发布,以避免重复应用混入

使用案例

定义一个 mixin.js

 let MiXin = {
data(){
return {
name: 'zhangning'
}
},
created(){
console.log('这是mixin中的name', this.name)
},
mounted(){},
methods: {}
}
export default MiXin
 // 全局引入

import mixin from './mixin'
Vue.mixin(mixin)
// 在 vue 组件中局部引入
import '@/mixin'
export default {
mixins: [mixin]
}

混入和组件的合并注意事项

1.数据对象 data 在内部进行递归合并,在和组件的数据发生冲突时以组件数据优先

2.同名钩子函数(created,mounted)将混合为一个数组,都将被调用。另外混入对象的钩子将在组件自身钩子之前调用

3.值为对象的选项(methods,components,directives)将被混合为同一个对象,两个对象键名冲突时,去组件对象的键值对

 

10.Vue.compile() -- 模板渲染

将一个模板字符串编译成 render 函数。旨在完整版时可用

let res = Vue.compile('<div><span>{{ massage }}</span></div>')

new Vue({

data: {

message: 'hello'

},

render: res.render,

staticRenderFns: res.staticRenderFns

})

以上是官网给的一个小实例。

以后深入理解之后,再回来更新

 

11.Vue.observable() -- 可用于组件间共享数据

让一个对象可响应,Vue 内部会用它来处理 data 函数返回的对象。

返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时出发相应的更新。

处理简单的跨组件共享数据状态的问题,可以说是个精简的 vuex

示例

创建store.js

 import Vue from 'vue'
export const store = Vue.observable({num: 0})
export const mutations = {
setNum (num) {
store.num = num
}
}

在组件中使用(所有的说明都没有举例子来的实在,理解的更快)

 <template>
<div>
<span>选择数量</span>
<button @click="setNum(num + 1)"> + </button>
<span>count</span>
<button @click="setNum(num - 1)"> - </button>
</div>
</template>
<script>
import { store, mutations } from '@/store/store'
export default {
name: 'numIndex',
computed: {
count() {
return store.num
}
},
methods: {
setNum: mutations.setNum
}
}
</script>

 

12.Vue.version()

提供字符串形式的 Vue 安装版本号。这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略

let version = Number(Vue.version.split('.')[0])

if (version == 2) {

} else if (version == 1) {

} else {}

就是获取当前使用的 vue 版本号,原理就是读取 package.json 中的 version 字段

 

以上就是 vue API 全局 API 的所有内容,

宝剑锋从磨砺出,梅花香自苦寒来。

版权声明
本文为[张宁187]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/zhangning187/p/vueapiqjapizj.html

  1. [front end -- JavaScript] knowledge point (IV) -- memory leakage in the project (I)
  2. This mechanism in JS
  3. Vue 3.0 source code learning 1 --- rendering process of components
  4. Learning the realization of canvas and simple drawing
  5. gin里获取http请求过来的参数
  6. vue3的新特性
  7. Get the parameters from HTTP request in gin
  8. New features of vue3
  9. vue-cli 引入腾讯地图(最新 api,rocketmq原理面试
  10. Vue 学习笔记(3,免费Java高级工程师学习资源
  11. Vue 学习笔记(2,Java编程视频教程
  12. Vue cli introduces Tencent maps (the latest API, rocketmq)
  13. Vue learning notes (3, free Java senior engineer learning resources)
  14. Vue learning notes (2, Java programming video tutorial)
  15. 【Vue】—props属性
  16. 【Vue】—创建组件
  17. [Vue] - props attribute
  18. [Vue] - create component
  19. 浅谈vue响应式原理及发布订阅模式和观察者模式
  20. On Vue responsive principle, publish subscribe mode and observer mode
  21. 浅谈vue响应式原理及发布订阅模式和观察者模式
  22. On Vue responsive principle, publish subscribe mode and observer mode
  23. Xiaobai can understand it. It only takes 4 steps to solve the problem of Vue keep alive cache component
  24. Publish, subscribe and observer of design patterns
  25. Summary of common content added in ES6 + (II)
  26. No.8 Vue element admin learning (III) vuex learning and login method analysis
  27. Write a mini webpack project construction tool
  28. Shopping cart (front-end static page preparation)
  29. Introduction to the fluent platform
  30. Webpack5 cache
  31. The difference between drop-down box select option and datalist
  32. CSS review (III)
  33. Node.js学习笔记【七】
  34. Node.js learning notes [VII]
  35. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  36. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  37. 【JQuery框架,Java编程教程视频下载
  38. [jQuery framework, Java programming tutorial video download
  39. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  40. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  41. 【Vue,阿里P8大佬亲自教你
  42. 【Vue基础知识总结 5,字节跳动算法工程师面试经验
  43. [Vue, Ali P8 teaches you personally
  44. [Vue basic knowledge summary 5. Interview experience of byte beating Algorithm Engineer
  45. 【问题记录】- 谷歌浏览器 Html生成PDF
  46. [problem record] - PDF generated by Google browser HTML
  47. 【问题记录】- 谷歌浏览器 Html生成PDF
  48. [problem record] - PDF generated by Google browser HTML
  49. 【JavaScript】查漏补缺 —数组中reduce()方法
  50. [JavaScript] leak checking and defect filling - reduce() method in array
  51. 【重识 HTML (3),350道Java面试真题分享
  52. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  53. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  54. [re recognize HTML (3) and share 350 real Java interview questions
  55. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  56. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  57. 【重识 HTML ,nginx面试题阿里
  58. 【重识 HTML (4),ELK原来这么简单
  59. [re recognize HTML, nginx interview questions]
  60. [re recognize HTML (4). Elk is so simple