简介

了解 Babel 插件基本知识,理解按需加载的内部原理,再也不怕面试官问我按需加载的实现原理了。


import { Button } from 'element-ui'

怎么就变成了

var Button = require('element-ui/lib/button.js')
require('element-ui/lib/theme-chalk/button.css')

为了找到答案,分两步来进行,这也是自己学习的过程:

  1. babel 插件入门,编写 babel-plugin-lyn 插件

  2. 解读 babel-plugin-component 源码,从源码中找到答案

babel 插件入门

这一步我们去编写一个babel-plugin-lyn插件,这一步要达到的目的是:

  • 理解babel插件做了什么

  • 学会分析AST语法树

  • 学会使用基本的API

  • 能编写一个简单的插件,做基本的代码转换

有了以上基础我们就可以尝试去阅读babel-plugin-component源码,从源码中找到我们想要的答案

简单介绍

Babel是一个JavaScript编译器,是一个从源码到源码的转换编译器,你为Babel提供一些JavaScript代码,Babel按照要求更改这些代码,然后返回给你新生成的代码。

代码转换(更改)的过程中是借助AST (抽象语法树)来完成的,通过改变AST节点信息来达到转换代码的目的,到这里其实也就可以简单回答出我们在目标中提到的代码转化是怎么完成的 ?,其实就是Babel读取我们的源代码,将其转换为AST,分析AST,更改AST的某些节点信息,然后生成新的代码,就完成了转换过程,而具体是怎么更改节点信息,就需要去babel-plugin-component源码中找答案了

Babel的世界中,我们要更改某个节点的时候,就需要去访问(拦截)该节点,这里采用了访问者模式访问者是一个用于AST遍历的跨语言的模式,加单的说就是定义了一个对象,用于在树状结构获取具体节点的的方法,这些节点其实就是AST节点,可以在 AST Explorer 中查看代码的AST信息,这个我们在编写代码的时候会多次用到

babel-plugin-lyn

接下来编写一个自己的插件

初始化项目目录

mkdir babel-plugin && cd babel-plugin && npm init -y

新建插件目录

在项目的node_modules目录新建一个文件夹,作为自己的插件目录

mkdir -p node_modules/babel-plugin-lyn

在插件目录新建 index.js

touch index.js

创建需要被处理的 JS 代码

在项目根目录下创建 index.js,编写如下代码

let a = 1
let b = 1

很简单吧,我们需要将其转换为:

const aa = 1
const bb = 1

接下来进行插件编写

babel-plugin-lyn/index.js

基本结构
// 函数会有一个 babelTypes 参数,我们结构出里面的 types
// 代码中需要用到它的一些方法,方法具体什么意思可以参考 
// https://babeljs.io/docs/en/next/babel-types.html
module.exports = function ({ types: bts }) {// 返回一个有 visitor 的对象,这是规定,然后在 visitor 中编写获取各个节点的方法return {visitor: {...}}
}
分析源代码

有了插件的基本结构之后,接下来我们需要分析我们的代码,它在AST中长什么样

AST Explorer

如下图所示:

用鼠标点击需要更改的地方,比如我们要改变量名,则点击以后会看到右侧的AST tree展开并高亮了一部分,高亮的这部分就是我们要改的变量aAST节点,我们知道它是一个Identifier类型的节点,所以我们就在visitor中编写一个Identifier方法

module.exports = function ({ types: bts }) {return {visitor: {/*** 负责处理所有节点类型为 Identifier 的 AST 节点* @param {*} path AST 节点的路径信息,可以简单理解为里面放了 AST 节点的各种信息* @param {*} state 有一个很重要的 state.opts,是 .babelrc 中的配置项*/Identifier (path, state) {// 节点信息const node = path.node// 从节点信息中拿到 name 属性,即 a 和 bconst name = node.name// 如果配置项中存在 name 属性,则将 path.node.name 的值替换为配置项中的值if (state.opts[name]) {path.node.name = state.opts[name]}}}}
}

这里我们用到了插件的配置信息,接下来我们在.babelrc中编写插件的配置信息

.babelrc
{"plugins": [["lyn",{"a": "aa","b": "bb"}]]
}

这个配置项是不是很熟悉?和babel-plugin-component的及其相似,lyn表示 babel 插件的名称,后面的对象就是我们的配置项

输出结果
首先安装 babel-cli

这里有一点需要注意,在安装 babel-cli 之前,把我们编写的插件备份,不然执行下面的安装时,我们的插件目录会被删除,原因没有深究,应该是我们的插件不是一个有效的 npm 包,所以会被清除掉

npm i babel-cli -D
编译
npx babel index.js

得到如下输出:

let aa = 1;
let bb = 1;

说明我们的插件已经生效,且刚才的思路是没问题的,转译代码其实就是通过更改 AST 节点的信息即可

let -> const

我们刚才已经完成了变量的转译,接下来再把let关键字变成const

按照刚才的方法,我们需要更改关键字let,将光标移动到let上,发现AST Tree高亮部分变了,可以看到letAST节点类型为VariableDeclaration,且我们要改的就是kind属性,好了,开始写代码

module.exports = function ({ types: bts }) {return {visitor: {Identifier (path, state) {...},// 处理变量声明关键字VariableDeclaration (path, state) {// 这次就没从配置文件读了,来个简单的,直接改path.node.kind = 'const'}}}
}
编译
npx babel index.js

得到如下输出:

const aa = 1;
const bb = 1;

到这里我们第一阶段的入门就结束了,是不是感觉很简单??是的,这个入门示例真的很简单,但是真的编写一个可用于业务Babel插件以及其中的涉及到的AST编译原理是非常复杂的。但是这个入门示例已经可以支持我们去分析babel-plugin-component插件的源码原理了。

完整代码
// 函数会有一个 babelTypes 参数,我们结构出里面的 types
// 代码中需要用到它的一些方法,方法具体什么意思可以参考 
// https://babeljs.io/docs/en/next/babel-types.html
module.exports = function ({ types: bts }) {// 返回一个有 visitor 的对象,这是规定,然后在 visitor 中编写获取各个节点的方法return {visitor: {/*** 负责处理所有节点类型为 Identifier 的 AST 节点* @param {*} path AST 节点的路径信息,可以简单理解为里面放了 AST 节点的各种信息* @param {*} state 有一个很重要的 state.opts,是 .babelrc 中的配置项*/Identifier (path, state) {// 节点信息const node = path.node// 从节点信息中拿到 name 属性,即 a 和 bconst name = node.name// 如果配置项中存在 name 属性,则将 path.node.name 的值替换为配置项中的值if (state.opts[name]) {path.node.name = state.opts[name]}},// 处理变量声明关键字VariableDeclaration (path, state) {// 这次就没从配置文件读了,来个简单的,直接改path.node.kind = 'const'}}}
}

babel-plugin-component 源码分析

目标分析

在进行源码阅读之前我们先分析一下我们的目标,带着目标去阅读,效果会更好

源代码

// 全局引入
import ElementUI from 'element-ui'
Vue.use(ElementUI)
// 按需引入
import { Button, Checkbox } from 'element-ui'
Vue.use(Button)
Vue.component(Checkbox.name, Checkbox)

上面就是我们使用element-ui组件库的两种方式,全局引入和按需引入

目标代码

// 全局引入
var ElementUI = require('element-ui/lib')
require('element-ui/lib/theme-chalk/index.css')
Vue.use(ElementUI)
// 按需引入
var Button = require('element-ui/lib/button.js')
require('element-ui/lib/theme-chalk/button.css')
var Checkbox = require('element-ui/lib/checkbox.js')
require('element-ui/lib/theme-chalk/checkbox.css')
Vue.use(Button)
Vue.component(Checkbox.name, Checkbox)

以上就是源代码和转译后的目标代码,我们可以将他们分别复制到 AST Explorer 中查看 AST Tree的信息,进行分析

全局引入

从上图中可以看出,这两条语句总共是由两种类型的节点组成,import对应的ImportDeclaration的节点,Vue.use(ElementUI)对应于ExpressionStatement类型的节点

可以看到import ElementUI from 'element-ui'对应到AST中,from后面的element-ui对应于source.value,且节点类型为StringLiteral

import ElementUI from 'element-ui'中的ElementUI对应于ImportDefaultSpecifier类型的节点,是个默认导入,变量对应于Indentifier节点的name属性

6

Vue.use(ElementUI)是个声明式的语句,对应于ExpressionStatement的节点,可以看到参数ElementUI放到了arguments部分

按需引入

可以看到body有三个子节点,一个ImportDeclaration,两个ExpressionStatement,和我们的代码一一对应

import语句中对于from后面的部分上面的全局是一样的,都是在source中,是个Literal类型的节点

可以看到import后面的内容变了,上面的全局引入是一个ImportDefaultDeclaration类型的节点,这里的按需加载是一个ImportDeclaration节点,且引入的内容放在specifiers对象中,每个组件(Button、Checkbox)是一个ImportSpecifier,里面定义了importedlocalIdentifier,而我们的变量名称(Button、Checkbox)放在name属性上

剩下的Vue.use(Button)Vue.component(Checkbox.name, Checkbox)和上面全局引入类似,有一点区别是Vue.component(Checkbox.name, Checkbox)arguments有两个元素

经过刚开始的基础入门以及上面对于AST的一通分析,我们其实已经大概可以猜出来从源代码目标代码这个转换过程中发生了些什么,其实就是在visitor对象上设置响应的方法(节点类型),然后去处理符合要求的节点,将节点上对应的属性更改为目标代码上响应的值,把源代码目标代码都复制到 AST Explorer 中查看,就会发现,相应节点之间的差异(改动)就是babel-plugin-component做的事情,接下来我们进入源码寻找答案。

源码分析

直接在刚才的项目中执行

npm i babel-plugin-component -D

安装 babel-plugin-component,安装完成,在node_modules目录找babel-plugin-component目录

image-20220207175041085

看代码是随时对照AST Explorer和打log确认

.babelrc

{"plugins": [["component",{"libraryName": "element-ui","styleLibraryName": "theme-chalk"}]]
}

入口,index.js

// 默认就是用于element-ui组件库的按需加载插件
module.exports = require('./core')('element-ui');

核心,core.js

源码阅读提示

  • 清楚读源码的目的是什么,为了解决什么样的问题
  • 一定要有相关的基础知识,比如上面的 babel 入门,知道入口位置在 visitor,以及在 visitor 中找那些方法去读
  • 读过程中一定要勤动手,写注释,打 log,这样有助于提高思路
  • 阅读这篇源码,一定要会用 AST Explorer 分析和对比我们的源代码 和 目标代码
  • 下面的源代码几乎每行都加了注释,大家按照步骤自己下一套源码,可以对比着看,一遍看不懂,看两遍,书读三遍其义自现,真的,当然,读的过程中有不懂的地方需要查一查
/*** 判断 obj 的类型* @param {*} obj */
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); 
}// 提供了一些方法,负责生成 import 节点
var _require = require('@babel/helper-module-imports'),addSideEffect = _require.addSideEffect,addDefault = _require.addDefault;// node.js 的内置模块,处理 路径信息
var resolve = require('path').resolve;// node.js 内置模块,判断文件是否存在
var isExist = require('fs').existsSync;// 缓存变量, cache[libraryName] = 1 or 2
var cache = {};
// 缓存样式库的样式路径,cachePath[libraryName] = ''
var cachePath = {};
// importAll['element-ui/lib'] = true,说明存在默认导入
var importAll = {};module.exports = function core(defaultLibraryName) {return function (_ref) {// babelTypes,提供了一系列方法供使用,官网地址:https://babeljs.io/docs/en/next/babel-types.htmlvar types = _ref.types;// 存储所有的 ImportSpecifier,即按需引入的组件,specified = { Button: 'Button', Checkbox: 'Checkbox' }var specified;// 存储所有全局引入的库,libraryObjs = { ElementUI: 'element-ui' }var libraryObjs;// 存储已经引入(处理)的方法(组件),// selectedMethods = {//   ElementUI: { type: 'Identifier', name: '_ElementUI' },//   Button: { type: 'Identifier', name: '_Button' },//   Checkbox: { type: 'Identifier', name: '_Checkbox' }// }var selectedMethods;// 引入的模块和库之间的对应关系,moduleArr = { Button: 'element-ui', Checkbox: 'element-ui' }var moduleArr;// 将驼峰命名转换为连字符命名function parseName(_str, camel2Dash) {if (!camel2Dash) {return _str;}var str = _str[0].toLowerCase() + _str.substr(1);return str.replace(/([A-Z])/g, function ($1) {return "-".concat($1.toLowerCase());});}/*** 该方法负责生成一些 AST 节点,这些节点的信息是根据一堆配置项来的,这对配置项就是在告诉 AST 节点每个组件的路径信息,* 比如 'element-ui/lib/button.js' 和 'element-ui/lib/theme-chalk/button.css'* @param {*} methodName Button、element-ui* @param {*} file 一拖不想看的对象信息* @param {*} opts .babelrc 配置项*/function importMethod(methodName, file, opts) {// 如果 selectedMethods 中没有 Butotn、element-ui 则进入 if ,否则直接 return selectedMethods[methodName],说明该方法(组件)已经被处理过了if (!selectedMethods[methodName]) {var options;var path;// 不用管if (Array.isArray(opts)) {options = opts.find(function (option) {return moduleArr[methodName] === option.libraryName || libraryObjs[methodName] === option.libraryName;}); // eslint-disable-line}/*** 以下是一堆配置项*/// 传递进来的配置options = options || opts;var _options = options,// 配置的 libDir_options$libDir = _options.libDir,// 没有配置,就默认为 lib, /element-ui/lib/button.js 中的 lib 就是这么来的libDir = _options$libDir === void 0 ? 'lib' : _options$libDir,// 组件库,element-ui_options$libraryName = _options.libraryName,// 组件库名称libraryName = _options$libraryName === void 0 ? defaultLibraryName : _options$libraryName,// 样式,boolean 类型,这里是 undefined_options$style = _options.style,// style 默认是 true,也可以由用户提供,在用户没有提供 styleLibraryName 选项是起作用style = _options$style === void 0 ? true : _options$style,// undefiendstyleLibrary = _options.styleLibrary,// undefined_options$root = _options.root,// ''root = _options$root === void 0 ? '' : _options$root,_options$camel2Dash = _options.camel2Dash,camel2Dash = _options$camel2Dash === void 0 ? true : _options$camel2Dash;// 配置项中的,'theme-chalk'var styleLibraryName = options.styleLibraryName;// ''var _root = root;var isBaseStyle = true;var modulePathTpl;var styleRoot;var mixin = false;// 后缀 xx.cssvar ext = options.ext || '.css';if (root) {_root = "/".concat(root);}if (libraryObjs[methodName]) {// 默认导入 ElementUI, path = 'element-ui/lib'path = "".concat(libraryName, "/").concat(libDir).concat(_root);if (!_root) {// 默认导入的情况下,记录在 importAll 中标记 path 为 trueimportAll[path] = true;}} else {// 按需引入,path = 'element-ui/lib/button'path = "".concat(libraryName, "/").concat(libDir, "/").concat(parseName(methodName, camel2Dash));}// 'element-ui/lib/button'var _path = path;/*** selectedMethods['Button'] = { type: Identifier, name: '_Button' }* addDefault 就负责添加刚才在 visitor.CallExpreesion 那说的那堆东西,* 这里主要负责 var Button = require('element-ui/lib/button.js'),* 这是猜的,主要是没找到这方面的文档介绍*/selectedMethods[methodName] = addDefault(file.path, path, {nameHint: methodName});/*** 接下来是处理样式*/if (styleLibrary && _typeof(styleLibrary) === 'object') {styleLibraryName = styleLibrary.name;isBaseStyle = styleLibrary.base;modulePathTpl = styleLibrary.path;mixin = styleLibrary.mixin;styleRoot = styleLibrary.root;}// styleLibraryName = 'theme-chalk',如果配置该选项,就采用默认的方式,进入 else 查看if (styleLibraryName) {// 缓存样式库路径if (!cachePath[libraryName]) {var themeName = styleLibraryName.replace(/^~/, '');// cachePath['element-ui'] = 'element-ui/lib/theme-chalk'cachePath[libraryName] = styleLibraryName.indexOf('~') === 0 ? resolve(process.cwd(), themeName) : "".concat(libraryName, "/").concat(libDir, "/").concat(themeName);}if (libraryObjs[methodName]) {// 默认导入/* istanbul ingore next */if (cache[libraryName] === 2) {// 提示信息,意思是说如果你项目既存在默认导入,又存在按需加载,则要保证默认导入在按需加载的前面throw Error('[babel-plugin-component] If you are using both' + 'on-demand and importing all, make sure to invoke the' + ' importing all first.');}// 默认导出的样式库路径:path = 'element-ui/lib/theme-chalk/index.css'if (styleRoot) {path = "".concat(cachePath[libraryName]).concat(styleRoot).concat(ext);} else {path = "".concat(cachePath[libraryName]).concat(_root || '/index').concat(ext);}cache[libraryName] = 1;} else {// 按需引入,这里不等于 1 就是存在默认导入 + 按需引入的情况,基本上没人会这么用if (cache[libraryName] !== 1) {/* if set styleLibrary.path(format: [module]/module.css) */var parsedMethodName = parseName(methodName, camel2Dash);if (modulePathTpl) {var modulePath = modulePathTpl.replace(/\[module]/ig, parsedMethodName);path = "".concat(cachePath[libraryName], "/").concat(modulePath);} else {path = "".concat(cachePath[libraryName], "/").concat(parsedMethodName).concat(ext);}if (mixin && !isExist(path)) {path = style === true ? "".concat(_path, "/style").concat(ext) : "".concat(_path, "/").concat(style);}if (isBaseStyle) {addSideEffect(file.path, "".concat(cachePath[libraryName], "/base").concat(ext));}cache[libraryName] = 2;}}// 添加样式导入,require('elememt-ui/lib/theme-chalk/button.css'),这里也是猜的,说实话,addDefault 方法看的有点懵,要是有文档就好了addDefault(file.path, path, {nameHint: methodName});} else {if (style === true) {// '/element-ui/style.css,这里是默认的,ext 可以由用户提供,也是用默认的addSideEffect(file.path, "".concat(path, "/style").concat(ext));} else if (style) {// 'element-ui/xxx,这里的 style 是用户提供的 addSideEffect(file.path, "".concat(path, "/").concat(style));}}}return selectedMethods[methodName];}function buildExpressionHandler(node, props, path, state) {var file = path && path.hub && path.hub.file || state && state.file;props.forEach(function (prop) {if (!types.isIdentifier(node[prop])) return;if (specified[node[prop].name]) {node[prop] = importMethod(node[prop].name, file, state.opts); // eslint-disable-line}});}function buildDeclaratorHandler(node, prop, path, state) {var file = path && path.hub && path.hub.file || state && state.file;if (!types.isIdentifier(node[prop])) return;if (specified[node[prop].name]) {node[prop] = importMethod(node[prop].name, file, state.opts); // eslint-disable-line}}return {// 程序的整个入口,熟悉的 visitorvisitor: {// 负责处理 AST 中 Program 类型的节点Program: function Program() {// 将之前定义的几个变量初始化为没有原型链的对象specified = Object.create(null);libraryObjs = Object.create(null);selectedMethods = Object.create(null);moduleArr = Object.create(null);},// 处理 ImportDeclaration 节点ImportDeclaration: function ImportDeclaration(path, _ref2) {// .babelrc 中的插件配置项var opts = _ref2.opts;// import xx from 'xx', ImportDeclaration 节点var node = path.node;// import xx from 'element-ui',这里的 node.source.value 存储的就是 库名称var value = node.source.value;var result = {};// 可以不用管,如果配置项是个数组,从数组中找到该库的配置项if (Array.isArray(opts)) {result = opts.find(function (option) {return option.libraryName === value;}) || {};}// 库名称,比如 element-uivar libraryName = result.libraryName || opts.libraryName || defaultLibraryName;// 如果当前 import 的库就是我们需要处理的库,则进入if (value === libraryName) {// 遍历node.specifiers,里面放了多个ImportSpecifier,每个都是我们要引入的组件(方法)node.specifiers.forEach(function (spec) {// ImportSpecifer 是按需引入,还有另外的一个默认导入,ImportDefaultSpecifier,比如:ElementUIif (types.isImportSpecifier(spec)) {// 设置按需引入的组件, 比如specfied['Button'] = 'Button'specified[spec.local.name] = spec.imported.name;// 记录当前组件是从哪个库引入的,比如 moduleArr['Button'] = 'element-ui'moduleArr[spec.imported.name] = value;} else {// 默认导入,libraryObjs['ElementUI'] = 'element-ui'libraryObjs[spec.local.name] = value;}});// 不是全局引入就删掉该节点,意思是删掉所有的按需引入,这个会在 importMethod 方法中设置if (!importAll[value]) {path.remove();}}},/*** 这里很重要,我们会发现在使用按需加载时,如果你只是import引入,但是没有使用,比如Vue.use(Button),则一样不会打包,所以这里就是来* 处理这种情况的,只有你引入的包实际使用了,才会真的import,要不然刚才删了就没有然后了,就不会在 node 上添加各种 arguments 了,比如:* {*   type: 'CallExpression',*   callee: { type: 'Identifier', name: 'require' },*   arguments: [ { type: 'StringLiteral', value: 'element-ui/lib' } ]* }* {*   type: 'CallExpression',*   callee: { type: 'Identifier', name: 'require' },*   arguments: [*    {*      type: 'StringLiteral',*      value: 'element-ui/lib/chalk-theme/index.css'*    }*   ]* }* {*    type: 'CallExpression',*    callee: { type: 'Identifier', name: 'require' },*    arguments: [ { type: 'StringLiteral', value: 'element-ui/lib/button' } ]* }* 以上这些通过打log可以查看,这个格式很重要,因为有了这部分数据,我们就知道:* import {Button} from 'element-ui' 为什么能* 得到 var Button = require('element-ui/lib/button.js')* 以及 require('element-ui/lib/theme-chalk/button.css')** @param {*} path * @param {*} state */CallExpression: function CallExpression(path, state) {// Vue.use(Button),CallExpression 节点var node = path.node;// 很大的一拖对象,不想看(不用看,费头发)var file = path && path.hub && path.hub.file || state && state.file;// callee 的 name 属性,我们这里不涉及该属性,类似ElementUI(ok)这种语法会有该属性,node.callee.name 就是 ElementUIvar name = node.callee.name;console.log('import method 处理前的 node:', node)// 判断 node.callee 是否属于 Identifier,我们这里不是,我们的是一个 MemberExpressionif (types.isIdentifier(node.callee)) {if (specified[name]) {node.callee = importMethod(specified[name], file, state.opts);}} else {// 解析 node.arguments 数组,每个元素都是一个 Identifier,Vue.use或者Vue.component的参数node.arguments = node.arguments.map(function (arg) {// 参数名称var argName = arg.name;// 1、这里会生成一个新的 Identifier,并更改 AST节点的属性值// 2、按需引入还是默认导入是在 ImportDeclaration 中决定的if (specified[argName]) {// 按需引入,比如:{ type: "Identifier", name: "_Button" },这是 AST 结构的 JSON 对象表示形式return importMethod(specified[argName], file, state.opts);} else if (libraryObjs[argName]) {// 默认导入,{ type: "Identifier", name: "_ElementUI" }return importMethod(argName, file, state.opts);}return arg;});}console.log('import method 处理后的 node:', node)},/*** 后面几个不用太关注,在这里不涉及,看字面量就可以明白在做什么 */// 处理 MemberExpression,更改 node.object 对象MemberExpression: function MemberExpression(path, state) {var node = path.node;var file = path && path.hub && path.hub.file || state && state.file;if (libraryObjs[node.object.name] || specified[node.object.name]) {node.object = importMethod(node.object.name, file, state.opts);}},// 处理赋值表达式AssignmentExpression: function AssignmentExpression(path, _ref3) {var opts = _ref3.opts;if (!path.hub) {return;}var node = path.node;var file = path.hub.file;if (node.operator !== '=') return;if (libraryObjs[node.right.name] || specified[node.right.name]) {node.right = importMethod(node.right.name, file, opts);}},// 数组表达式ArrayExpression: function ArrayExpression(path, _ref4) {var opts = _ref4.opts;if (!path.hub) {return;}var elements = path.node.elements;var file = path.hub.file;elements.forEach(function (item, key) {if (item && (libraryObjs[item.name] || specified[item.name])) {elements[key] = importMethod(item.name, file, opts);}});},// 属性Property: function Property(path, state) {var node = path.node;buildDeclaratorHandler(node, 'value', path, state);},// 变量声明VariableDeclarator: function VariableDeclarator(path, state) {var node = path.node;buildDeclaratorHandler(node, 'init', path, state);},// 逻辑表达式LogicalExpression: function LogicalExpression(path, state) {var node = path.node;buildExpressionHandler(node, ['left', 'right'], path, state);},// 条件表达式ConditionalExpression: function ConditionalExpression(path, state) {var node = path.node;buildExpressionHandler(node, ['test', 'consequent', 'alternate'], path, state);},// if 语句IfStatement: function IfStatement(path, state) {var node = path.node;buildExpressionHandler(node, ['test'], path, state);buildExpressionHandler(node.test, ['left', 'right'], path, state);}}};};
};

总结

通过阅读源码以及打log的方式,我们得到了如下信息:

{type: 'CallExpression',callee: { type: 'Identifier', name: 'require' },arguments: [ { type: 'StringLiteral', value: 'element-ui/lib' } ]
}
{type: 'CallExpression',callee: { type: 'Identifier', name: 'require' },arguments: [{type: 'StringLiteral',value: 'element-ui/lib/chalk-theme/index.css'}]
}
{type: 'CallExpression',callee: { type: 'Identifier', name: 'require' },arguments: [ { type: 'StringLiteral', value: 'element-ui/lib/button' } ]
}

这其实就是经过变化后的AST的部分信息,通过对比目标代码在AST Tree中的显示会发现,结果是一致的,也就是说通过以上AST信息就可以生成我们需要的目标代码

目标代码中的require关键字就是calleerequire函数中的参数就是arguments数组

以上就是 按需加载原理分析 的所有内容。

链接

  • 组件库专栏
  • AST Explorer
  • @babel/types
  • @babel/helper-module-imports

感谢各位的:点赞收藏评论,我们下期见。


当学习成为了习惯,知识也就变成了常识,扫码关注微信公众号,共同学习、进步。文章已收录到 github,欢迎 Watch 和 Star。

微信公众号

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 解决Logitech G HUB一直在加载问题

    解决Logitech G HUB卡一直在加载问题 图文教程请参考我的个人博客: https://www.928wang.cn/archives/209.html 今天遇到了G HUB一直在加载,怎么也进不去,一直在转可能是由于杀毒软件或其他优化的软件把罗技的自动更新服务关了,导致G Hub在…...

    2024/4/30 23:13:22
  2. 三方API接口

    手机号码归属地API接口: https://www.juhe.cn/docs/api/id/11 历史上的今天API接口: https://www.juhe.cn/docs/api/id/63 股票数据API接口: https://www.juhe.cn/docs/api/id/21 全国WIFI接口: https://www.juhe.cn/docs/api…...

    2024/4/16 20:29:17
  3. 挖矿木马事件

    ...

    2024/4/20 1:24:11
  4. 微软4000亿收购动视暴雪,背后逻辑是什么?

    本文参考新摘商业评论“微软买的不是暴雪,而是开启科技未来的锁钥”一文。 近日,IT巨头微软宣布花费678亿美元(约4000多亿元人民币)收购以开发游戏开娱乐互动为主的动视暴雪公司,收购金额之巨,为游戏界之最…...

    2024/4/13 10:12:05
  5. 必看的C语言教程之头文件那点事

    🍊博客主页:Geekwyz的江湖背景 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 🌟本文由 Geekwyz原创 🙏作者水平很有限,如果发现错误,欢迎留言 目录1.编译预处理命令2.多…...

    2024/4/19 12:03:28
  6. Java程序设计基础【2】

    1.下列关于构造方法定义的描述中,错误的是 A.在方法名的前面没有返回值类型的声明 B.在方法中不能使用return语句返回一个值 C.方法名与类名相同 D.当定义了有参构造方法,系统默认的无参构造方法依然存在 2.下面哪种类型的文件可以在Java虚拟机中运行 …...

    2024/4/13 10:12:10
  7. CMakeList模板(二):编译多个工程

    CMakeList模板(二):编译多个工程 文章标签: cmake CMakeList 本文链接:https://blog.csdn.net/lianshaohua/article/details/107783811 性能优化教程:https://www.brendangregg.com/overview.html c 专栏收录该内容 编译多个库和…...

    2024/4/13 10:12:30
  8. 信息系统项目管理师Part4-网络协议

    应用层 FTP 文件传输协议是网络上两台计算机传送文件的协议,FTP在客户机和服务器之间需建立两条TCP连接,一条用于传送控制信息(使用21号端口),另一条用于传送文件内容(使用20号端口) TFTP 简…...

    2024/4/15 10:34:19
  9. 【UVM芯片漫游指南_000】总目录(下)——UVM目录

    说明 此文为路科验证红宝书《芯片验证漫游指南》的读书笔记本书基于UVM1.2UVM参考网址:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/ 【UVM芯片漫游指南_000】总目录(下)——UVM目录: 【UVM芯片漫游指南_0…...

    2024/4/13 10:12:50
  10. 编译原理:CH4 静态语义分析

    CH4 静态语义分析 文章目录CH4 静态语义分析4.1 语法制导翻译4.1.1 语法与语义(1)语法与语义的关系(2)语义分析的两个作用4.1.2 属性与语义规则(1)语法制导翻译的基本思想(2)属性的抽…...

    2024/4/18 6:09:56
  11. 《材料力学》在线作业

    一、单选题 (共 10 道试题,共 30 分) 1.设轴向拉伸杆横截面的正应力为σ,则45度斜截面上的正应力和剪应力分别为()。 A.σ/2、σ B.均为σ C.σ、σ/2 D.均为σ/2 2.在同一减速箱中,设高速轴的直径为d1、低速轴的直径为d2&#xf…...

    2024/4/20 17:01:17
  12. 深入解析Java对象和类在HotSpot VM内部的具体实现

    本篇讨论Java对象和类在HotSpot VM内部的具体实现,探索虚拟机在底层是如何对这些Java语言的概念建模的。 对象与类 HotSpot VM使用oop描述对象,使用klass描述类,这种方式被称为对象类二分模型。理解对象类二分模型最好的方法是回归到编程语言…...

    2024/4/28 8:11:43
  13. Java程序设计基础【4】

    1.Java配置环境变量path的目的是为了可以查找到.class文件。 2.while、do…while、for循环语句都可以进行嵌套,并且它们之间也可以互相嵌套。 3.定义一个方法时,其访问修饰符、返回值类型、方法名的顺序可以互换。 4.Java语言有三种技术平台&#xff…...

    2024/4/7 22:36:27
  14. RGB颜色 取色器/拾色器 颜色混搭

    取色器/拾色器 https://www.runoob.com/tags/html-colorpicker.html HueHexRgbHslHsv0#ff0000rgb(255, 0, 0)hsl(0, 100%, 50%)hsv(0, 100%, 100%)15#ff4000rgb(255, 64, 0)hsl(15, 100%, 50%)hsv(15, 100%, 100%)30#ff8000rgb(255, 128, 0)hsl(30, 100%, 50%)hsv(30, 100%, …...

    2024/4/18 10:14:48
  15. 工业机器人协作控制研究

    当前工业焊接大多由工业机器人完成。在弧焊焊接领域,传统的由焊接机器人变位机工装夹具组成的焊接工作站已不能满足当前小批量、定制化的柔性自动化生产需求。而由多个机器人构成的协作焊接系统具有更强的作业能力、更大范围的工作空间、更灵活的系统结构和组织方式…...

    2024/4/13 10:12:50
  16. JavaScript中var、let和const的区别

    一、前言 在ES6(ES2015)出现之前,JavaScript中声明变量就只有通过 var 关键字,函数声明是通过 function 关键字,而在ES6之后,声明的方式有 var 、 let 、 const 、 function 、 class ,本文主要…...

    2024/4/15 18:08:04
  17. HTML编程笔记

    HTML5和CSS笔记 HTML详解 1 初识HTML Hyper Text Markup Language(超文本标记语言) 超文本包括:文字、图片、音频、视频、动画等。 Html5Css3 —>现在使用的版本 Html5提供了一些新的元素和一些有趣的新特性,同时也建立了一…...

    2024/4/13 10:12:55
  18. 权限修饰符

    权限修饰符 修饰符同一个类中同一个包中子类无关类不同包的子类不同包的无关类Private√默认√√Protected√√√Public√√√√ 以形容词解释: Public公共的Private私人的Protected受保护的...

    2024/4/5 2:28:55
  19. 写作高质量文章的八种技巧

    爱发猫认为高质量的文章是有明确目标的内容,以最快、最易理解的方式提供读者想要的信息。通过调整目标和文章主题之间的平衡,创建适当的结构和标题,并在不模糊方向的情况下完成文本,可以创建低退出率的高质量文章。 本文介绍了增…...

    2024/4/13 10:12:40
  20. 本文深入探讨虚拟机运行时的java线程启动、停止、睡眠与中断

    Java线程 上节描述了虚拟机中各式各样的线程及其创建过程,其中尤为重要的是JavaThread,它是Java线程java.lang.Thread在JVM层的表示,包含很多重要数据。 JavaThread持有一个指向java.lang.Thread对象的指针,即oop(Ja…...

    2024/4/13 10:12:45

最新文章

  1. SparkSQL编程入口和模型与SparkSQL基本编程

    SparkSQL编程入口和模型 SparkSQL编程模型 主要通过两种方式操作SparkSQL,一种就是SQL,另一种为DataFrame和Dataset。 1)SQL:SQL不用多说,就和Hive操作一样,但是需要清楚一点的是,SQL操作的是表&#xf…...

    2024/5/6 21:15:58
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. Vue3学习笔记+报错记录

    文章目录 1.创建Vue3.0工程1.1使用vue-cli创建1.2 使用vite创建工程1.3.分析Vue3工程结构 2.常用Composition2.1 拉开序幕的setup2.2 ref函数_处理基本类型2.3 ref函数_处理对象类型2.4 ref函数使用总结 1.创建Vue3.0工程 1.1使用vue-cli创建 查看vue/cli版本,确保…...

    2024/5/5 23:44:14
  4. vue中内置指令v-model的作用和常见使用方法介绍以及在自定义组件上支持

    文章目录 一、v-model是什么二、什么是语法糖三、v-model常见的用法1、对于输入框(input):2、对于复选框(checkbox):3、对于选择框(select):4、对于组件(comp…...

    2024/5/4 20:45:28
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57