关于vue2.x 和 vue3的相关
为什么data是一个函数?
vue中data必须是函数是为了保证组件的独立性和复用性,data是一个函数,当组件实例化的时候这个函数将会被调用,返回一个对象,计算机会给这个对象返回一个内存地址,实例化该组件几次,就会分配几个内存地址,他们的地址都不一样,所以各个组件中的数据不会相互干扰,改变其中一个组件的状态,其他组件不变
Vue组件通信方式
1:prop和$emit父子组件之间的通信
2:$parent,$children 获取当前组件的父组件/子组件,
疑问:是获取所有子组件吗?-
当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。 如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
3:$attrs和$listeners,
待补充:
4:在父组件中通过provide提供变量,在子组件/孙组件中都可以通过inject来注入变量。
待补充:应用场景可适用于全局状态的管理
5:通过$refs 获取组件实例及状态
6:envetBus 兄弟组件数据传递 这种情况下可以使用事件总线的方式
7:Vuex
Vue相关内置指令
怎样理解 Vue 的单向数据流
数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
子组件中通过this.$emit('parentFun','changedData')修改
注意:在子组件直接用 v-model 绑定父组件传过来的 prop 这样是不规范的写法 开发环境会报警告
如果实在要改变父组件的 prop 值 可以再 data 里面定义一个变量 并用 prop 的值初始化它 之后用$emit 通知父组件去修改
computed 和 watch 的区别和运用的场景
computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容,它可以设置 getter 和 setter。
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
计算属性一般用在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来;而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑
v-if 与 v-for 为什么不建议一起使用
v-for 和 v-if 不要在同一个标签中使用,因为解析时先解析 v-for 再解析 v-if。如果遇到需要同时使用时可以考虑写成计算属性的方式。
Vue2.x 响应式数据的原理
整体思路是数据劫持+观察者模式
对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。
class Observer {
// 观测值
constructor(value) {
this.walk(value);
}
walk(data) {
// 对象上的所有属性依次进行观测
let keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let value = data[key];
defineReactive(data, key, value);
}
}
}
// Object.defineProperty数据劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
observe(value); // 递归关键
// --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
// 思考?如果Vue数据嵌套层级过深 >>性能会受影响
Object.defineProperty(data, key, {
get() {
console.log("获取值");
//需要做依赖收集过程 这里代码没写出来
return value;
},
set(newValue) {
if (newValue === value) return;
console.log("设置值");
//需要做派发更新过程 这里代码没写出来
value = newValue;
},
});
}
export function observe(value) {
// 如果传过来的是对象或者数组 进行属性劫持
if (
Object.prototype.toString.call(value) === "[object Object]" ||
Array.isArray(value)
) {
return new Observer(value);
}
}
Vue.mixin 的使用场景和原理
在日常开发中会经常遇到在不同的组件中有相同的代码和逻辑,这些代码的功能相对独立,可以通过Vue的Mixin抽离公共的业务逻辑,原理类似于“对象的继承”,当组件初始化时会调用mergeOptions方法进行合并,采用策略模式针对不同的属性进行合并,当组件和混入对象有同名选项时,这些选项将以恰当的方式进行合并。
相关实现代码:
/*
* @Author: cq
* @Date: 2021-07-24 00:35:13
* @LastEditTime: 2021-07-24 00:53:45
*/
export default function initMixin(Vue) {
Vue.mixin = function (mixin) {
//合并对象
this.options = mergeOptions(this.options, mixin)
}
}
// src/util/index.js
// 定义生命周期
export const LIFECYCLE_HOOKS = [ "beforeCreate", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "beforeDestroy", "destroyed",];
//合并策略
const starts = {};
//mixin核心方法
export function mergeOptions(parent, child) {
const options = {};
//遍历父组件
for (let k in parent) {
mergeFiled(k);
}
//父亲没有,儿子有
for (let k in child) {
if (parent.hasOwnProperty(k)) {
mergeFiled(k)
}
}
//核心:合并字段的方法
function mergeFiled(k) {
if (starts[k]) {
options[k] = starts[k](parent[k], child[k]);
} else {
//默认策略
options[k] = child[k] ? child[k] : parent[k];
}
}
return options;
}
nextTick的使用场景和原理
nextTick中的回调是在下次DOM循环更新之后执行的延迟回调,在修改数据后立即使用这个方法,获取更新之后的DOM。 主要思路是采用微任务优先的方式调用异步方法去执行nextTick。
使用场景如下:
<el-input ref="label" v-show="showLabel" v-model="ruleForm.label"></el-input>
<i @click="btnOpen"></i>
methods:{
btnOpen(){
this.showLabel = true
this.$nextTick(()=>{
this.$refs.label.focus()
})
}
}
底层代码实现原理:
let callbacks = [];
let pending = false;
function flushCallbacks() {
pending = false; //把标志还原为false
// 依次执行回调
for (let i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
}
let timerFunc; //定义异步方法 采用优雅降级
if (typeof Promise !== "undefined") {
// 如果支持promise
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
};
} else if (typeof MutationObserver !== "undefined") {
// MutationObserver 主要是监听dom变化 也是一个异步方法
let counter = 1;
const observer = new MutationObserver(flushCallbacks);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else if (typeof setImmediate !== "undefined") {
// 如果前面都不支持 判断setImmediate
timerFunc = () => {
setImmediate(flushCallbacks);
};
} else {
// 最后降级采用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
export function nextTick(cb) {
// 除了渲染watcher 还有用户自己手动调用的nextTick 一起被收集到数组
callbacks.push(cb);
if (!pending) {
// 如果多次调用nextTick 只会执行一次异步 等异步队列清空之后再把标志变为false
pending = true;
timerFunc();
}
}
Vue set方法原理
在两种情况下修改数据Vue是不会触发视图更新
1:在实例创建后添加新的属性到实例上(给响应式数据新增属性)
2:直接更改数组的下标来修改数组的值
Vue.set或者this.$set原理如下:
因为响应式数据,对象和数组数据都增加了__ob__属性,代表的是Observer实例。当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪,然后会触发对象__ob__的dep收集到的watcher去更新,当修改数组时我们调用数组本身的splice方法去更新数组。
原理实现代码:
vue Router中常用的路由模式实现原理
1:hash模式:
1:location.hash的值实际就是URL中#后面的东西,它的特点在于:hash虽然出现在url中,但不会被包含在HTTP请求中,对后段完全没有影响,因此改变hash不会重新加载页面。
2:可以为hash的改变添加监听事件,
window.addEventListener('hashChange',funcRef,false);
每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录,利用hash的特点,就可以实现更改前端路由“更新试图但不会重新加载页面“的功能。
2:history模式:
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。
这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。
Vuex为什么要分模块并添加命名空间
模块:由于使用单一状态树,应用的所有状态会集中到一个比较大的对象上,当应用变得非常复杂时,store对象会变得非常臃肿。为解决以上问题,Vuex允许我们将store分为模块(moudle)。
高精度权限控制(Vue.directive)
自定义指令 directive
应用的场景:判断当前登录账号是否包含某个角色。来控制元素的显示/隐藏
1:首先建立一个全局js文件,用于存放相关权限判断的函数
export function checkArray (key) {
let arr = ['1', '2', '3', '4', 'demo']
let index = arr.indexOf(key)
if (index > -1) { return true // 有权限 }
else { return false // 无权限 }
}
2:在main.js中,
//main.js
import { checkArray } from "./common/array";
Vue.directive("permission",{
inserted(el,binding){
let permission = binding.value;//获取到实例绑定的值
if(permission){
let hasPermission = checkArray(permission);
if(!hasPermission){
//没有权限 移除dom元素
el.parentNode && el.parentNode.removeChild(el);
}
}
}
})
Vue3.0中,为什么用Proxy 替代 defineProperty API?
defineProperty存在的弊端
1:vue2.x是通过Object.defineProperty中的getter和setter函数进行数据劫持完成数据响应的。
2:无法直接监听对象属性的新增和删除
3:无法直接监听数组
Proxy的优势
1:相当于给一个对象的外层添加了一层拦截,
2:因为是对整个对象的外层做了一层拦截,可以监听到属性的新增和删除
3:Proxy可以监听到数组的变化
Proxy的理解
Vue3.0编译做了哪些优化?
过滤器的使用
filter mp.weixin.qq.com/s/0Yekkc08o…
动态指令参数
Vue 2.6的最酷功能之一是可以将指令参数动态传递给组件。我们可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
隐藏的大招--hook
开发过程中我们有时候要创建一个定时器,在组件被销毁之前,这个定时器也要销毁。代码如下:
mounted() {
this.timer = setInterval(()=>{
console.log('time'+new Date().getTime());
},1000)
}
beforeDestroy(){
if(this.timer){
console.log('销毁定时器');
clearInterval(this.timer);
}
}
上面这种写法有个很明显的弊端:定时器timer的创建和清理并不是在一个地方,这样很容易导致忘记去清理!
let timer = setInterval(()=>{
console.log('time'+new Date().getTime());
},1000)
this.$once("hook:beforeDestroy",function(){
if(timer){
clearInterval(timer);
timer = null;
}
})
封装过哪些通用组件(重要:)?
个人认为,封装高质量可复用性强的公众代码是对业务逻辑的深刻理解和代码的精通。
封装过哪些通用方法?
常用的一些代码方法:
例如:
1:深拷贝方法
2:
ts
es6+ 的相关知识准备
检测数据类型
1:Array.isArray(value)
2: instanceof
function foo(){}
var a = new foo()
console.log(a instanceof foo); // true
3: typeof value typeof一般返回以下几个字符串:
"number", "string","boolean","object","function","undefined"
对于Array,Null等特殊对象使用typeof一律返回object,这正是typeof的局限性。
4:Objetc.prototype.toString() 精确判断对象的数据类型
const a = 123;
const b = [1,2,3];
console.log(Object.prototype.toString.call(a));
console.log(Object.prototype.toString.call(b));
//[object Number]
//[object Array]
Object.assin()是深拷贝还是浅拷贝
结论:
1:如果对象的属性是简单类型,(string,number)类型,那么得到的对象是深拷贝
2:如果属性值是对象或者其他引用数据类型,那么得到的对象是浅拷贝
for循环和forEach使用return是否跳出整个循环
for 使用return 、 break,是跳出了整个循环。
forEach 使用return只是跳出了当前的循环, 使用break报语法错误。
想要使用跳出循环可以使用some()
原型链(prototype)继承
js中使用原型链来实现继承
概念1:所有引用类型都有一个对象特征,都具有一个__proto__属性,属性值是一个普通的对象
概念2: 函数都有一个prototype 属性
概念3:引用类型的__proto__指向构造函数的prototype
js的执行机制
Event Loop 事件循环,是指浏览器或者Node的一种解决javascript单线程运行时不会阻塞的一种机制,
在 javascript中,任务被分为两种,一种宏任务(MacroTask),一种也叫微任务(MircroTask)
宏任务:
setTimeout setInterval scripts全部代码
微任务:
Process.nextTick(Node) (优先于Promise)
Promise
javascript中有一个main thread主线程和call-stack调用栈,所有任务都会被放到调用栈等待主线程执行。
执行顺序:
js中单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程一次执行,异步任务会在异步任务有了结果之后,将注册的回调函数放入任务队列中等待主线程空闲的时候。被读取到栈内等待主线程的执行。
执行栈在执行完成同步任务之后,查看执行栈是否为空,如果执行栈为空,就会去检查微任务队列是否为空,如果为空的话,就会执行Task(宏任务),否则就一次性执行完成所有微任务。
每次单个宏任务执行完成之后,检查微任务队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务后,设置微任务为null,然后再执行宏任务,如此循环。
Array.reduce()
应用场景:数组的去重,求和
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
reducer 函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
数组去重的几种方法
1: set
2: reduce配合,indexOf 循环
IIFE(立即执行函数相关)
css3
三角形的实现
BFC(块级格式化上下文)
用于清除浮动,防止margin重叠
形成BFC的条件 1:根元素 2:overflow的值不为visible 3: float不为none 4: display的值为inline-block、table-cell、table-caption 5:position的值为absolute或fixed
1:解决外边距重叠的问题
外层包裹元素设置overflow,形成BFC
2:解决浮动的问题
FLUTTER
微信小程序相关
微信小程序的生命周期
onLoad() 页面加载时触发,只会调用一次,可获取当前页面中的参数
onShow() 页面显示/切入前台时触发,一般用来发送请求数据;
onReady() 页面初次渲染完成时触发,只会调用一次,代表页面已可以和视图层进行交互;
onHide() 页面隐藏、切入后台时触发,如底部tab切换到其他页面或小程序切入后台
onUnload() 页面卸载时触发,如redirectTo或navigateBack到其他页面;
微信小程序的架构原理
小程序本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面进行,可以通过微信客户端调用原生的接口。它的架构,是数据驱动的架构模式,它的UI和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现; 它从技术讲和现有的前端开发差不多,采用JavaScript、WXML、WXSS三种技术进行开发; 功能可分为webview和appService两个部分; webview用来展现UI,appService有来处理业务逻辑、数据及接口调用; 两个部分在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染、事件的处理等。
小程序的双向绑定和vue的异同
大体相同,但小程序直接this.data的属性是不可以同步到视图的,必须调用this.setData()方法!
微信小程序中的js运行环境和浏览器js的运行环境有什么不同?
微信小程序js运行环境是jsCore中,没有window和document对象
微信小程序怎样跟事件传值
待补充:
给 HTML 元素添加
data-*属性来传递我们需要的值,然后通过e.currentTarget.dataset或onload的param参数获取。但data -名称不能有大写字母和不可以存放对象
小程序关联微信公众号如何确定用户的唯一性
待补充:
使用 wx.getUserInfo方法 withCredentials 为 true 时 可获取 <encryptedData,里面有 union_id。后端需要进行对称解密
微信小程序 个人账号,搭建部署
相关
微信登录授权相关
nodejs相关
JS的线程与进程
单个JS运行时是一个进程,有1个主线程,和其他子线程(异步回调和setTimeOut,ajax等) node:实质上是多线程的但是主线程只有一个(所以有人说node是单线程的),还有其他子线程
JS中的堆和栈
nodeJS中常用的一些模块
process
1:process是一个全局的变量,所以使用的时候是不需要执行require操作,可以直接使用
这里分两部分来说明:第一个就是可以借助它去获取进程信息,比如进程工作的时候是一个什么样的环境,通过process可以获取。第二个通过process可以对当前的进程做一些操作,比如可以监听进程执行过程中的内置事件,创建子进程完成更多的操作。
2: 内存相关获取
// 查看内存消耗
console.log(process.memoryUsage());
/**
* rss: 常驻内存
* heapToal: 总内存大小
* heapUsed: 已使用内存
* external: 扩展内存 - 底层模块占用的C/C++核心模块
* arrayBuffers: 缓冲区大小
*/
3: CPU相关信息获取
console.log(process.cpuUsage());
/** * user: 用户占用的时间片段 * system: 系统占用的时间片段 */
4:通过process查看运行目录,Node环境,cpu架构,用户环境,系统平台
process.cwd();// 运行目录
process.version // Node版本
process.versions //运行环境版本
process.env.Node_ENV //环境
process.env.PATH //环境变量
process.env.USERPROFILE; // 管理员目录路径 不同环境方式不一样 process.env.HOME process.platform; // 平台 win32 macos
5:运行时可以获取启动参数,PID,运行时间
process.argv; // 获取运行参数,空格分隔可在数组中获取到,默认会存在Node目录和执行脚本的目录两个值。
process.argv0; // 获取第一个值, 只有这一个api
process.pid; // 获取运行的pid
process.ppid;
process.uptime; // 脚本运行时间
事件监听在process中提供的内容。这里不会着重说明process里面到底有哪些事件,主要还是看一看在NodeJS里面熟悉一下事件驱动编程以及发布订阅的模式。
process是实现了emit接口的。可以使用on监听事件,内部提供了很多的事件,比如exit,程序退出的时候执行。这里绑定的事件只能执行同步代码,是不可以执行异步代码的,这里要注意。
事件监听在`process`中提供的内容。这里不会着重说明`process`里面到底有哪些事件,主要还是看一看在`NodeJS`里面熟悉一下事件驱动编程以及发布订阅的模式。
`process`是实现了`emit`接口的。可以使用`on`监听事件,内部提供了很多的事件,比如`exit`,程序退出的时候执行。这里绑定的事件只能执行同步代码,是不可以执行异步代码的,这里要注意。
6:标准输入,输出,错误
process.stdout //是一个流,可以对它进行读写操作
process.stdout.write('123')
cosnt fs = require('fs');
fs.createReadStream('text.txt').pipi(process.stdout) //读取文件输出
process.stdin; // 可以拿到控制台输入的内容
process.stdin.pipe(process.stdout); // 输入之后输出
process.stdin; // 可以拿到控制台输入的内容
process.stdin.pipe(process.stdout); // 输入之后输出
path
Node中的内置模块,可以直接使用require将它导入,主要作用是处理文件的目录和路径。
const path = require('path')
1:basename()
path.basename(__filename);//当前文件名称
// 传入第二个参数如果匹配会省略后缀,不匹配仍旧返回真实的后缀
path.basename(__filename, '.js'); // test
2:dirname()
path.dirname(__filename); // d:\Desktop\test\
3: extname()
path.extname(__filename); // .js
path.extname('/a/b'); //
path.extname('/a/b/index.html.js.css'); // .css
path.extname('/a/b/index.html.js.'); // .
4:isAbsolute()
path.isAbsolute('a'); // false
path.isAbsolute('/a'); // true
5:join()
拼接多个路径片段,还原成完整可用路径
path.join('a/b', 'c', 'index.html'); // a/b/c/index.html
path.join('/a/b', 'c', 'index.html'); // /a/b/c/index.html
path.join('a/b', 'c', '../', 'index.html'); // a/b/index.html
6:resolve()
path.resolve() //获取绝对路径
7:parse()
解析路径
/**
* root: /
* dir: /a/b/c
* base: index.html
* ext: .html
* name: index
*/
8:format()
序列化路径,与parse功能相反,将对象拼接成完整的路径
root: '/',
dir: '/a/b/c',
base: 'index.html',
ext: '.html',
name: 'index'
});
// /a/b/c/index.html
9:normalize()
规范化路径,将不可用路径变为可用路径
path.normalize('a/b/c/d'); // a/b/c/d
path.normalize('a//b/c../d'); // a/b/c../d
path.normalize('a\\/b/c\\/d'); // a/b/c/d
Buffer
fs
fs是一个内置的核心模块,所有文件相关的操作都是通过fs来进行实现的,比如文件以及目录的创建,删除,信息的查询或者文件的读取和写入。
文件系统的基础知识:权限位,标识符,文件描述等
权限是指当前的操作系统内不同的用户角色对于当前的文件可以执行的不同权限操作,文件的权限操作被分为r,w,x三种, r是读权限,w是写权限,x是执行权限。如果用8进制的数字进行表示r是4,w是2,x是1,如果不具备该权限就是一个0。
操作系统中将用户分为三类分别是文件的所有者,一般指的是当前用户自己,再有就是文件的所属组,类似当前用户的家人,最后是其他用户也就是访客用户。
Node中flag表示对文件操作方式,比如是否可读可写。
r: 表示可读
w: 表示可写
s: 表示同步
+: 表示执行相反操作
x: 表示排他操作
a: 表示追加操作
fd就是操作系统分配给被打开文件的标识,通过这个标识符文件操作就可以识别和被追踪到特定的文件。不同操作系统之间是有差异的,Node为我们抹平了这种差异。
Node每操作一个文件,文件描述符就会递增一次,并且它是从3开始的。因为0,1,3已经被输入,输出和错误占用了。后面我们在使用fs.open打开文件的时候就会得到这个fd。
fs任何文件操作api都有同步和异步两种方式,这里只演示异步API,同步基本也相同
1:文件读写
readFile:从指定文件中读取数据
const path = require('path');
fs.readFile(path.resolve('aaa.txt'), 'utf-8', (err, data) => {
console.log(err);
console.log(data);
})
writeFile:向制定文件中写入数据
(会替换掉原有文件的数据)
fs.writeFile('bbb.txt', 'hello', {
mode: 438, // 操作位
flag: 'w+',
encoding: 'utf-8'
}, (err) => {
console.log(err);
})
appendFile:追加的方式向指定的文件中写入数据
console.log(err);
})
copyFile:将文件中的数据拷贝到另一个文件
console.log(err);
})
watchFile:对指定文件进行监控
interval: 20 // 20ms监控一次
}, (curr, prev) => {
console.log(curr); // 当前信息
console.log(prev); // 前一次信息
if (curr.mtime !== prev.mtime) {
// 文件被修改了
}
})
fs.unwatchFile('bbb.txt'); // 取消监控
2:文件的打开与关闭
readFile和writeFile的工作机制是将文件里的内容一次性的全部读取或者写入到内存中,而这种方式对于大体积文件来说是不合理的,因此需要一种实现边写边读或者边读边写的操作方式,这时就需要文件的打开,读取,写入,关闭看做是各自独立的环节。
const path = require('path');
// open
fs.open(path.resolve('aaa.txt'), 'r', (err, fd) => {
console.log(err);
console.log(fd);
fs.close(fd, (err) => {
});
})
目录操作
access:判断文件或目录是否具有操作权限
console.log(err); // 存在错误就是没有权限
})
stat:获取目录及文件信息
fs.stat('aaa.txt', (err, stat) => {
console.log(stat); // size isFile(), isDirectory()
})
mkdir:创建目录
fs.mkdir('a/b/c', {
recursive: true, // 递归创建
}, (err) => {
console.log(err);
})
rmdir:删除目录
fs.rmdir('a', {
recursive: true, // 递归删除
}, (err) => {
console.log(err);
})
readdir:读取目录中内容,不会递归子组件
fs.readdir('a', (err, files) => {
console.log(files);
})
unlink:删除指定文件
fs.unlink('a', (err) => {
console.log(err);
})
commonjs
CommonJs 的出现是为了解决前端模块化
Events
Node中通过EventEmitter类实现事件统一管理。这个类大部分供内置模块使用,比如fs,http都内置了这个模块。
Node是基于事件驱动的异步操作架构,内置events模块,模块提供了EventEmitter类,它的实例对象具备注册事件和发布事件,删除事件的常规操作。
const EventEmitter = require('events');
const ev = new EventEmitter();
ev.on('event', () => {
})
ev.emit('event');
事件队列
const Event = require('events').EventEmitter;
let ev = new Event();
// 绑定事件
ev.on('msg', function(a, b,c) {
console.log(a, b, c); // 12 , 3, 8
})
// 派发事件
ev.emit('msg', 12,3,8);
stream
使用流可以从空间和时间上提升效率,Nodejs诞生之初就是为了提高IO性能,其中最常用的文件系统和网络就是流操作的应用者。
NodeJs中流就是处理流式数据的抽象接口,NodeJs中的stream对象提供了用于操作流的对象。
Readable: 可读流,能够实现数据的获取。
Writeable: 可写流,能够实现数据的写操作。
Duplex: 双工流,即可度又可写。
Tranform: 转换流,可读可写,还能实现数据转换。
const fs = require('fs');
const rs = fs.createReadStream('./a.txt');
const ws = fs.createWriteStream('./b.txt');
rs.pipe(ws);
1:可读流
生产供消费的数据流。
const rs = fs.createReadStream('./a.txt');
const { Readable } = require('stream');
const source = ['a', 'b', 'c'];
class MyReadable extends Readable {
constructor() {
super();
this.source = source;
}
_read() {
const data = this.source.shift() || null;
this.push(data);
}
}
const myreadable = new MyReadable(source);
myreadable.on('data', (chunk) => {
console.log(chunk.toString());
})
2:可写流 用于消费数据的流,响应
const ws = fs.createWriteStream('./b.txt');
const { Writable } = require('stream');
class MyWriteable extends Writable {
constructor() {
super();
}
_write (chunk, en, done) {
process.stdout.write(chunk.toString());
process.nextTick(done);
}
}
const mywriteable = new MyWriteable();
mywriteable.write('yindong', 'utf-8', () => {
consoel.log('end');
})
3:Duplex
const { Duplex } = require('stream');
class MyDuplex extends Duplex {
constructor(source) {
super();
this.source = source;
}
_read() {
const data = this.source.shift() || null;
this.push(data);
}
_write(chunk, en, next) {
process.stdout.write(chunk);
process.nextTick(next);
}
}
const source = ['a', 'b', 'c'];
const myDuplex = new MyDuplex(source);
mtDuplex.on('data', (chunk) => {
console.log(chunk.toString());
})
mtDuplex.write('yindong', () => {
console.log('end');
})
4:Transform
const { Transform } = require('stream');
class MyTransform extends Transform {
constructor() {
super();
}
_transform(chunk, en, cb) {
this.push(chunk.toString().toUpperCase());
cb(null);
}
}
const t = new MyTransform();
t.write('a');
t.on('data', (chunk) => {
console.log(chunk.toString());
})
链表
链表是一种数据存储结构。
在文件可写流的write方法工作的时候,有些被写入的内容需要在缓冲区排队等待的,而且遵循的是先进先出的规则,为了保存这些排队的数据,在新版的Node中就采用了链表的结构来存储这些数据。
链表是由一系列节点组成的集合。这里的节点都称为Node节点,每个节点的身上都有一个对象的引用是指向下一个节点。将这些指向下一个节点的引用组合到一起也就形成了一个链。 对于链表结构来说我们常听到的会有不同类型,双向链表,单向链表。循环链表。常用的一般是双向链表。
Assertion
断言,如果不符合,会停止程序,并打印错误/
C++ Addons
使用c语言写的插件。在Node中使用。
Cluster
多线程
一个程序就是一个进程,一个进程会有多个线程,进程和线程是严格隔离的,拥有独立的执行空间,同一个进程内的所有线程共享一套空间,代码。
1:多进程
成本高,速度慢,安全,进程间通信麻烦,写代码简单
2:多线程
成本低,速度快,不安全,线程间通信简单,写代码复杂
Child_Processes, Cluster, Process。
Command Line Options
获取命令行参数
Crypto
签名,完成加密算法
const cryptp = require("crypto");
let obj = cryto.createHash('md5');
obj.update('123');
console.log(obj.digest('hex')); // MD5 32位
OS
系统相关
const os = require('os');
// 获取cpu个数
os.cpus();
url
请求url模块
const url = require('url');
url.parse('url', true); // 会解析出url和参数,包含query-string的功能。
Net
DNS
域名解析成ip
const dns = require('dns');
dns.resolve('baidu.com', (err, res) => {
})
http
const http = require('http');
const server = http.createServer((request, response) => {
// request 为请求数据
// response 为响应数据
// 设置返回值类型
res.writeHead(200, {'Content-type' : 'text/html'});
response.write = 'server start';
return response.end(); // 结束
});
server.listen(3000); // 服务监听的端口号