当前位置:
首页
文章
前端
详情

网易微专业高级前端开发工程师2022克鲁斯卡尔算法和普里姆算法

网易微专业高级前端开发工程师2022克鲁斯卡尔算法和普里姆算法

create-vue的实现原理 源代码 既然命令vue@x是通过npm init执行的,那么npm init做了什么?它怎么知道有vue@x这个命令呢? 这是一个性的问题。先解决吧。 1.npm init vue@x 第一个问题,npm init做了什么?从npm官网对npm init的描述可以知道,执行npm init的时候会转换这个命令。 NPM init[-force |-f |-yes |-y |-scope] npm init(与“npx /create”相同) npm init [/](与npx [/]create-)相同) 复制代码 换句话说

npm init foo -> npx create-foo NPM init @ usr/foo-> npx @ usr/create-foo npm init @usr -> npx @usr/create

那么结论就是npm init vue@x实际执行的是npx create-vue@x,@后面的参数就是vue的对应版本。核心是npx create-vue,create-vue是真正执行的命令。 知道了create-vue是真正的命令,那么第二个问题就变成了,npx怎么知道有create-vue? 先说结论:npx会在node_modules/的目录下检查命令。bin/和环境变量$PATH。如果它找不到命令,就会远程下载同名的依赖项,通过Node执行。

这个结论是看了阮一峰的npx教程后得出的。后面的【扩展】里也引用了这篇文章。

这对应于node_modules/。bin/create-vue。 看到这里,我以为package.json中bin字段定义的命令是在全局或者局部node_modules/中注册的。bin/目录。 create-vue命令确实是在这个项目的package.json的bin字段中定义的。 至此,npm init做过的事情都明白了,可以继续往下看了。

这里其实还有一个问题。@之后的版本号是怎么生效的? 执行npx create-vue@2时,npm会认为下载的大版本是2的最新版本,@3表示大版本是3的最新版本。

2.命令的条目文件 我知道create-vue命令的定义。 // package.json " bin": { " create-vue": "outfile.cjs " } 复制代码 但是项目里没有找到outfile.cjs这个文件,然后我就想如果会不会是打包的文件,就全局搜索了一下。果然在scripts/build.mjs文件中有一个定义,输出文件是outfile.cjs,对应的导入文件是index.ts 等待esbuild.build({ 捆绑:真的, 入口点:['index.ts'], outfile: 'outfile.cjs ', 格式:“cjs”, 平台:“节点”, 目标:“节点14”, ... }) 复制代码

问题出现了:cjs是什么文件? Cj代表commonJs,mjs代表ModuleJs。 参考文件:有什么区别。js和。mjs文件?

知道了命令的入口文件,我们来看看这个命令做了什么,是如何实现的。 3.命令的执行 index.ts文件中定义了五个函数,其核心是在init()函数中实现的,所以先以init()函数为起点,后面再看其他函数的具体实现。 3.1.初始化变量

首先通过process.cwd()获取当前工作目录,然后通过minimist获取命令行参数,类似于执行npm init vue@3 - ts。 //可能的选项: // -默认值 // - typescript / - ts // - jsx // -路由器/-vue-路由器 // -皮尼亚 // - with-tests / - tests(等于- vitest - cypress) // - vitest // -柏树 // - eslint //-eslint-with-beauty(为简单起见,仅通过eslint支持beauty) // - force(用于强制覆盖) const argv = minimixt(process . argv . slice(2),{ //这里包含了所有参数对应的别名 别名:{ typescript: ['ts'], with-tests': ['tests'], 路由器:['vue-router'] }, //所有参数都被视为布尔值 布尔值:真 }) 复制代码 process.argv得到的是一个数组,第一个元素是启动Node.js进程的可执行文件所在的绝对路径;第二个元素是当前执行的js文件的绝对路径;第三和随后的元素是参数。

通过demo看一下minimist的参数和返回值。 $ node example/minimal it . js-a beep-b boop { _: [],a:'哔',b: 'boop' } ​ $ node example/minimal it . js-x 3-y 4-n5-ABC-beep = boop foo bar baz { _: [ 'foo ',' bar ',' baz' ], x: 3, y: 4, n: 5, 答:没错, 是的, 列车员:是的, 哔声:“boop” }

定义了isFeatureFlagsUsed变量,如果已经在命令行中配置了该变量,则该变量用于跳过用户选择的以下步骤(提示的功能)。 const isFeatureFlagsUsed = 类型( argv.default?? argv.ts?? argv.jsx?? argv.router?? 阿根廷山羊?? argv.tests?? argv.vitest?? argv.cypress?? argv.eslint )=== '布尔型' 复制代码

??函数:当左操作数为空或未定义时,返回右操作数,否则返回左操作数。

尝试从命令行获取项目名称。如果不存在,将采用默认的项目名称。 设targetDir = argv。_[0] const defaultProjectName =!targetDir?vue-project ':目标目录 复制代码

这里需要注意的是argv的前两个元素。_和process.argv默认是一样的,但是在这里定义argv的时候是通过process.argv.slice(2)得到的,得到的结果是不一样的。

最后,定义forceOverwrite变量来决定是否强制覆盖和清空目录。 const force overwrite = argv . force//NPM init vue @ 3-force 复制代码

3.2.获取用户配置

首先,定义变量result并声明其类型。其次,它通过提示与用户进行交互。 结果=等待提示( [ { 名称:“项目名称”, 类型:targetDir?空:“文本”, 消息:“项目名称:”, initial: defaultProjectName, onState:(state)= >(targetDir = String(state . value)。trim() || defaultProjectName) }, { 名称:“应覆盖”, type:()= >(canskip清空(targetDir) || forceOverwrite?空:“确认”), 消息:()=> { const dirForPrompt = targetDir === ','?当前目录“:目标目录' $ { targetDir }” 1 ​ 返回“${ dirForPrompt }不为空。删除现有文件并继续吗?` } } ... ] ) 复制代码 下面是执行npm init vue@x命令后对用户的一些查询,包括项目名称,是否重写目录,是否引入vue-router/jsx等。具体内容在文章开头的图片中有描述。

这里有一个问题,就是为什么prompts的配置里有两个projectName的配置,因为这个是非阻塞的,后面会解决。

最后,将上述命令行中获得的参数与用户选择的参数相结合,并存储在相应的变量中。 常数{ 项目名称, packageName = projectName??defaultProjectName, shouldOverwrite = argv.force, needsJsx = argv.jsx, needsTypeScript = argv . typescript, needsRouter = argv.router, needsPinia = argv.pinia, needsCypress = argv . cypress | | argv . tests, needs vitest = argv . vitest | | argv . tests, needsEslint = argv . eslint | | argv[' eslint-with-beautiful '], needs prettier = argv[' eslint-with-prettle '] } =结果 const needsCypress CT = needsCypress & &!需求测试 复制代码

3.2.创建项目目录或清空现有目录以创建项目。 首先,将当前工作目录和项目名称合并到项目的根目录中。 后来如果目录存在,需要重写,就重写;否则,如果该目录不存在,将会创建它。 让我们来看看如何清空目录。该条目是emptyDir函数。 函数emptyDir(dir) { //如果不存在则返回 如果(!fs.existsSync(dir)) { 返回 } ​ postOrderDirectoryTraverse( dir, // fs.rmdirSync()只能删除空文件夹。 (dir) => fs.rmdirSync(dir), // fs.unlinkSync()只能删除文件或符号链接。 (file) => fs.unlinkSync(file) ) } 复制代码 这里再次调用postOrderDirectoryTraverse函数,删除文件夹和文件的回调函数被定义为参数。该功能的具体实现如下 导出函数postOrderDirectoryTraverse(dir,dirCallback,fileCallback) { // fs.readdirSync():读取该目录下的所有文件和目录。 for(fs . readdirsync(dir)的常量文件名){ // .git文件跳过 if(文件名=== '。git') { 继续 } ​ const fullpath = path.resolve(目录,文件名) // fs.lstatSync():返回文件或目录的信息。 // fs.lstatSync()。isDirectory():判断是否是目录,如果是,判断真假。 if (fs.lstatSync(fullpath))。isDirectory()) { //对于目录的操作:递归调用 postOrderDirectoryTraverse(完整路径,目录回调,文件回调) ​ //删除这个目录,fs.rmdirSync()只能删除一个空文件夹,所以最上面递归先删除最下面的文件夹。 目录回调(完整路径) 继续 } //如果是文件,则删除该文件。 fileCallback(完整路径) } } 复制代码

目录中的所有文件都通过fs.readdirSync()遍历,所有操作都在一个循环中进行。 然后判断如果是git文件,就跳过这个处理。 然后获取遍历项的绝对目录,判断该项是文件还是目录。如果是文件,则删除文件,如果是目录,则采用深度优先,逐步删除。

3.3.创建package.json const pkg = { name: packageName,version: '0.0.0' } fs . write file sync(path . resolve(root,' package.json '),JSON.stringify(pkg,null,2)) 复制代码 生成基本的package.json文件。 3.4.定义模板文件渲染函数,生成项目基本文件。 const render =函数render(templateName) { const templateDir = path . resolve(template root,templateName) renderTemplate(templateDir,root) } 复制代码

获取模板文件目录 为核心操作调用renderTemplate。这里你需要知道的就是渲染模板,具体实现在后面。 根据用户配置生成基本档案。

download:网易微专业高级前端开发工程师2022克鲁斯卡尔算法和普里姆算法

免责申明:本站发布的内容(图片、视频和文字)以转载和分享为主,文章观点不代表本站立场,如涉及侵权请联系站长邮箱:xbc-online@qq.com进行反馈,一经查实,将立刻删除涉嫌侵权内容。