当前位置: 首页 > news >正文

网站如何做内部链接wordpress建站Pdf

网站如何做内部链接,wordpress建站Pdf,网站后台文章排版,接单类型网站建设费用初探webpack之单应用多端构建 在现代化前端开发中#xff0c;我们可以借助构建工具来简化很多工作#xff0c;单应用多端构建就是其中应用比较广泛的方案#xff0c;webpack中提供了loader与plugin来给予开发者非常大的操作空间来操作构建过程#xff0c;通过操作中间产物…初探webpack之单应用多端构建 在现代化前端开发中我们可以借助构建工具来简化很多工作单应用多端构建就是其中应用比较广泛的方案webpack中提供了loader与plugin来给予开发者非常大的操作空间来操作构建过程通过操作中间产物我们可以非常方便地实现多端构建当然这是一种思想而不是深度绑定在webpack中的方法我们也可以借助其他的构建工具来实现比如rollup、vite、rspack等等。 描述 首先我们先来聊聊多端构建实际上单应用多端构建的思想非常简单就是在同一个项目中我们可以通过一套代码来构建出多个端的代码例如小程序的跨平台兼容、浏览器扩展程序的跨平台兼容、海内外应用资源合规问题等等这些场景的特点是核心代码是一致的只不过因为跨平台的原因会有接口调用或者实现配置的差异但是差异化的代码量是非常少的在这种场景下借助构建工具来实现单应用多端编译是非常合适的。 在这里需要注意的是我们是在编译的过程中处理掉单应用跨平台造成的代码冗余情况而例如在浏览器中不同版本的兼容代码是需要执行动态判断的不能够作为冗余处理因为我们不能够为每个版本的浏览器都分发一套代码所以这种情况不属于我们讨论的多端构建场景。实际上我们也可以理解为因为我们能够绝对地判断代码的平台并且能够独立分发应用包所以才可以在构建的过程中将代码分离兼容平台的代码不会消失只会转移相当于将代码中需要动态判断平台的过程从运行时移动到了构建时机从而能够获得更好的性能与更小的包体积。 接下来实现多端构建就需要借助构建工具的能力了通常构建工具在处理代码资源压缩时会有清除DEAD CODE的能力即使构建工具没有预设这个能力通常也会有插件来组合功能那么我们就可以借助这个方法来实现多端构建。那么具体来说我们可以通过if条件配合代码表达式让代码在编译的过程中保证是绝对的布尔值条件从而让构建工具在处理的过程中将不符合条件的代码处理掉DEAD CODE即可。此外由于我们实际上是处理了DEAD CODE那么在一些场景下例如对内与对外开放的SDK有不同的逻辑以及包引用等就可以借助构建工具的TreeShaking实现包体积的优化。 if (chromium chromium) {// xxx }if (gecko chromium) {// xxx }process.env 我们在平时开发的过程中特别是引入第三方Npm包的时候可能会发现打包之后会有出现ReferenceError: process is not defined的错误这也算是经典的异常了当然这种情况通常是发生在将Node.js代码应用到浏览器环境中除了这种情况之外在前端构建的场景中也会需要使用到process.env例如在React的入口文件react/index.js中就可以看到如下的代码: if (process.env.NODE_ENV production) {module.exports require(./cjs/react.production.min.js); } else {module.exports require(./cjs/react.development.js); }当然在这里是构建时发生的实际上还是运行在Node环境中的通过区分不同的环境变量打包不同的产物从而可以区分生产环境与开发环境的代码从而提供开发环境相关的功能和警告。那么类似的我们同样也可以借助这种方式作为多端构建的条件判断通过process.env来判断当前的平台从而在构建的过程中将不符合条件的代码处理掉。类似于React的这种方式来做跨平台编译当然是可行的只不过看起来这似乎是commonjs的模块化管理方式而ES Module是静态声明的语句也就是说导入导出语句必须在模块的顶层作用域中使用而不能在条件语句或循环语句等代码块中使用所以这段代码通常可能需要手动维护或者需要借助工具自动生成。 那么在ES Module静态声明中我们就需要借助共建工具来完成跨端编译的方案了。回到刚开始时提到的那个process is not defined的问题除了上述的两种情况还有一种常见的情况是process这个变量代码本身就存在于代码当中而在浏览器在runtime执行的时候发现并没有process这个变量从而抛出的异常。在最开始的时候我还是比较纳闷这个Node变量为什么会出现在浏览器当中所以为了解决这个问题我可能会在全局声明一下这个变量那么在现在看来当时我可能产生了误用的情况实际上我们应该借助于浏览器构建工具来处理当前的环境配置。那么我们来举个例子假设此时我们的环境变量是process.env.NODE_ENV是development而我们的源码中是这样的那么在借助打包工具处理之后这个判断条件就会变成development development这个条件永远为true那么else的部分就会变成DEAD CODE进而被移除由此最后我们实际得到的url是xxx同理在production的时候得到的url就会变成xxxxxx。 let url xxx; if (process.env.NODE_ENV development) {console.log(Development Env); } else {url xxxxxx; } export const URL url;// 处理后let url xxx; if (development development) {console.log(Development Env); }// else {// url xxxxxx;// } export const URL url;实际上这是个非常通用的处理方式通过指定环境变量的方式来做环境的区分以便打包时将不需要的代码移除例如在Create React App脚手架中就有custom-environment-variables相关的配置也就是必须要以REACT_APP_开头的环境变量注入并且NODE_ENV环境变量也会被自动注入当然值得注意的是我们不应该把任何私钥等环境变量的名称以REACT_APP_开头因为这样如果在前端构建的源码中有这个环境变量的使用则会造成密钥泄漏的风险这也是Create React App约定需要以REACT_APP_开头的环境变量才会被注入的原因。 那么实际上这个功能看起来是不是非常像字符串替换而webpack就提供了开箱即用的webpack.DefinePlugin来实现这个能力https://webpack.js.org/plugins/define-plugin/这个插件可以在打包的过程中将指定的变量替换为指定的值从而实现我们要做的允许跨端的的不同行为我们直接在webpack的配置文件中配置即可。此外使用ps -e或systemctl status查看进程pid并配合cat /proc/${pid}/environ | tr \0 \n来读取运行中程序的环境变量是个不错的方式。 new webpack.DefinePlugin({process.env.NODE_ENV: JSON.stringify(process.env.NODE_ENV),process.env.PLATFORM: JSON.stringify(process.env.PLATFORM), });说到这里就不得不提到package.json的sideEffects配置项了sideEffects通常被译作副作用当然我们也可以将其看作附带效应。在ES Module中顶部声明的模块是完全静态的也就是说整个模块的依赖结构在编译时是能够明确确定的那么通过确定的依赖来实现TreeShaking就是比较简单的事情了当然通过require以及import()等动态导入的方式就无法静态确定依赖结构了所以通常对于动态引用的模块不容易进行TreeShaking。那么假设我们现在实现了ES模块A并且引用了模块B而在B模块中实现的函数只用了其中一部分而另一部分在整个项目中并未使用那么这部分代码在静态分析之后就会被移除掉。 上边描述的是比较常规的情况实际上配合我们的process.env就可以更大程度地发挥这部分能力在不同的平台中通过环境变量封装不同的模块在打包的时候因为实际只引用但是并未调用所以整个模块都可以被TreeShaking假设我们有A - B - C三个模块如果能够在A处判断没有用到B也就是认为B是无副作用的模块那么通过打断B的引用便可以在包中省下来B模块与C模块的体积而实际上我们的模块引用深度可能是会相当大的这是个N叉树级的层次结构假如能在中间打断的话便可以很大程度上优化体积。 回到sideEffects的配置上假设我们的模块A引用了模块B而实际上在A中并没有任何关于B模块函数的调用只是单纯的引用了而已在B模块中实现了初始化的副作用代码例如直接在模块B中劫持了Node.prototype的函数注意在这里并没有将这个劫持封装到函数中是直接在模块中执行的。那么在默认情况下也就是package.json没有配置sideEffects默认为true即认为所有模块都有副作用的情况下B模块这段代码实际上同样会被执行而如果标记了sideEffects为false的情况下这段代码是不会被执行的。还有一种情况在写TS的时候我们可能通常不会写import type xxx from xxx;的语法在这种我们实际上仅引用了类型的情况下不必要的副作用代码还是会被执行的所以在这里sideEffects是很有必要的当然我们也可以通过Lint自动处理仅引用类型时的import type语句。 实际上配置sideEffects将直接配置为false的情况下通常是无法满足我们的需求的有些时候我们是直接引用了css类似于import ./index.css的这种形式因为没有实际的函数调用所以这段CSS也会被TreeShaking掉另外在开发环境下Webpack是默认不会开启TreeShaking的所以需要配置一下所以在很多Npm包中我们能够看到如下配置通常就是明确地标明了副作用模块避免意外的模块移除。 sideEffects: [dist/**/*,*.scss,*.less,*.css,**/styles/** ],__DEV__ 在阅读React和Vue的源码的时候我们通常可以看到__DEV__这个变量而如果我们观察仔细的话就可以发现虽然这是个变量但是并没有在当前文件中声明也没有从别的模块当中引入当然在global.d.ts中声明的不算因为其并不会注入到runtime中。那么实际上这个变量与process.env.NODE_ENV变量一样都是在编译时注入的起到的也是相通的作用只不过这个变量从命名中就可以看出来是比较关注于开发构建和生产构建之间的不同行为的定义。 实际上在这里这种方式相当于是另一种场景process.env是一种相对比较通用的场景也是大家普遍能够看懂的一种编译的定义方式而__DEV__比较像是内部自定义的变量所以这种方式比较适合内部使用。也就是说如果这个变量对应的行为是我们在开发过程和构建过程中内建的通常是在Npm包的开发过程中那么使用类似于__DEV__的环境变量是比较推荐的因为通常在打包的过程中我们会预定义好相关的值而不需要实际从环境变量中读取而且在打包之后相关代码会被抹掉不会引发额外的行为那么如果在构建的过程中需要用户自己来自定义的环境变量那么使用process.env是比较推荐的这是一种比较能为大家普遍认同的定义方式而且因为实际上可以通过环境变量来读取内容用户使用的过程中会更加方便。 那么在前边我们也说明了在webpack使用因为使用的是同样的方式只是简化了配置那么在这里我们也是类似的配置方式不知道大家有没有注意到一个细节我们使用的是JSON.stringify来处理环境变量的值这其实是一件很有意思的事情在之前实习的时候我也纳闷这个JSON.stringify的作用本来就是个字符串为什么还要stringify。实际上这件事很简单例如production这个字符串我们将其stringify之后便成为了production或者表示为\production\类似于将字符串又包裹了一层那么假如此时我们的代码如下: if (process.env.NODE_ENV development) {// xxx }那么重点来了我们之前提到了这种定义环境变量的方式是类似于字符串替换的模式而因为在JS的基本语法中如果我们传递的变量是字符串那么在实际输出的过程中会将其转换为字符串字面量例如如果我们执行console.log(production)输出的是production而执行console.log(\production\)输出的是production那么答案也就显而易见了如果不进行JSON.stringify的话在输出的源码当中会直接打印production而不是production从而在构建的过程中则会直接抛出异常因为我们并没有定义production这个变量。 console.log(production); // production console.log(production); // production console.log(\production\); // production// production编译后 if (production development) {// xxx } // \production\编译后 if (production development) {// xxx }那么现代化的构建工具通常都会有相关的处理方案而基于webpack封装的应用框架通常也可以直接定义底层的webpack配置从而将环境变量注入进去一些常见的构建工具配置方式如下: // webpack new webpack.DefinePlugin({__DEV__: JSON.stringify(process.env.NODE_ENV development),process.env.NODE_ENV: JSON.stringify(process.env.NODE_ENV),process.env.PLATFORM: JSON.stringify(process.env.PLATFORM), });// vite export default defineConfig({define: {__DEV__: JSON.stringify(process.env.NODE_ENV development),process.env.NODE_ENV: JSON.stringify(process.env.NODE_ENV),process.env.PLATFORM: JSON.stringify(process.env.PLATFORM),}, });// rollup import replace from rollup/plugin-replace; export default {plugins: [replace({values: {__DEV__: JSON.stringify(process.env.NODE_ENV development),process.env.NODE_ENV: JSON.stringify(process.env.NODE_ENV),process.env.PLATFORM: JSON.stringify(process.env.PLATFORM),},preventAssignment: true}),], };// rspack module.exports {builtins: {define: {__DEV__: JSON.stringify(process.env.NODE_ENV development),process.env.NODE_ENV: JSON.stringify(process.env.NODE_ENV),process.env.PLATFORM: JSON.stringify(process.env.PLATFORM),},} }if-def 在处理一些跨平台的编译问题时我最常用的的方法就是process.env与__DEV__但是在用多了之后发现在这种类似于条件编译的情况下大量使用process.env.PLATFORM xxx很容易出现深层次嵌套的问题可读性会变得很差毕竟我们的Promise就是为了解决异步回调的嵌套地狱的问题如果我们因为需要跨平台编译而继续引入嵌套问题的话总感觉并不是一个好的解决方案。 在C/C中有一个非常有意思的预处理器C Preprocessor不是编译器的组成部分但其是编译过程中一个单独的步骤简单来说C Preprocessor相当于是一个文本替换工具例如不加入标识符的宏参数等都是原始文本直接替换可以指示编译器在实际编译之前完成所需的预处理。#include、#define、#ifdef等等都属于C Preprocessor的预处理器指令在这里我们主要关注条件编译的部分也就是#if、#endif、#ifdef、#endif、#ifndef、#endif等条件编译指令。 #if VERBOSE 2print(trace message); #endif#ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */ # include unistd.h #elif defined _WIN32 /* _WIN32 is usually defined by compilers targeting 32 or 64 bit Windows systems */ # include windows.h #endif那么我们同样也可以将类似的方式借助构建工具来实现首先C Preprocessor是一个预处理工具不参与实际的编译时的行为那么是不是就很像webpack中的loader而原始文本的直接替换我们在loader中也是完全可以做到的而类似于#ifdef、#endif我们可以通过注释的形式来实现这样就可以避免深层次的嵌套问题而字符串替换的相关逻辑是可以直接修改原来来处理例如不符合平台条件的就可以移除掉符合平台条件的就可以保留下来这样就可以实现类似于#ifdef、#endif的效果了。此外通过注释来实现对某些复杂场景还是有帮助的例如我就遇到过比较复杂的SDK打包场景对内与对外以及对本体项目平台的行为都是不一致的如果在不构建多个包的情况下跨平台就需要用户自己来配置构建工具而使用注释可以在不配置loader的情况下同样能够完整打包在某些情况下可以避免用户需要改动自己的配置当然这种情况还是比较深地耦合在业务场景的只是提供一种情况的参考。 // #IFDEF CHROMIUM console.log(IS IN CHROMIUM); // #ENDIF// #IFDEF GECKO console.log(IS IN GECKO); // #ENDIF此外在之前实现跨平台相关需求的时候我发现使用预处理指令实现过多的逻辑反而不好特别是涉及到else的逻辑因为我们很难保证后续会不会需要兼容新的平台那么如果我们使用了else相关逻辑的话后续增删平台编译的时候就需要检查所有的跨平台分支逻辑而且比较容易忽略掉一些分支情况从而导致错误的发生所以在这里我们只需要使用#IFDEF、#ENDIF就可以了即明确地指出这段代码需要编译的平台由此来尽可能避免不必要的问题同时保留平台的扩展性。 那么接下来就需要通过loader来实现功能了在这里我是基于rspack来实现的同样兼容webpack5的基本接口当然在这里因为我们主要是对源代码进行处理所以使用的都是最基本的Api能力实际上在大部分情况下都是通用的。那么编写loader这部分就不需要过多描述了loader是一个函数接收源代码作为参数返回处理后的代码即可并且需要的相关信息可以直接从this中取得即可在这里我通过jsdoc将类型标注了一下。 const path require(path); const fs require(fs);/*** this {import(rspack/core).LoaderContext}* param {string} source* returns {string}*/ function IfDefineLoader(source) {return source; }接下来为了保持通用性我们处理一些参数包括读取的环境变量名字、include、exclude以及debug模式并且做一下匹配如果命中了该文件需要处理则继续否则直接返回源代码即可并且debug模式可以帮我们输出一些调试信息。 // 检查参数配置 /** type {boolean} */ const debug this.query.debug || false; /** type {(string|RegExp)[]} */ const include this.query.include || [path.resolve(src)]; /** type {(string|RegExp)[]} */ const exclude this.query.exclude || [/node_modules/]; /** type {string} */ const envKey this.query.platform || PLATFORM;// 过滤资源路径 let hit false; const resourcePath this.resourcePath; for (const includeConfig of include) {const verified includeConfig instanceof RegExp? includeConfig.test(resourcePath): resourcePath.startsWith(includeConfig);if (verified) {hit true;break;} } for (const excludeConfig of exclude) {const verified excludeConfig instanceof RegExp? excludeConfig.test(resourcePath): resourcePath.startsWith(excludeConfig);if (verified) {hit false;break;} } if (debug hit) {console.log(if-def-loader hit path, resourcePath); } if (!hit) return source;接下来就是具体的代码处理逻辑了最开始的时候我想使用正则的方式直接进行处理的但是发现处理起来比较麻烦尤其是存在嵌套的情况下就不太容易处理逻辑那么再后来我想反正代码都是 一行一行的逻辑按行处理的方式才是最方便的特别是在处理的过程中因为本身就是注释最终都是要删除的即使存在缩进的情况直接去掉前后的空白就能直接匹配标记进行处理了。这样思路就变的简单了很多预处理指令起始#IFDEF只会置true预处理指令结束#ENDIF只会置false而我们的最终目标实际上就是删除代码所以将不符合条件判断的代码行返回空白即可但是处理嵌套的时候还是需要注意一下我们需要一个栈来记录当前的处理预处理指令起始#IFDEF的索引即进栈当遇到#ENDIF再出栈并且还需要记录当前的处理状态如果当前的处理状态是true那么在出栈的时候就需要确定是否需要标记当前状态为false从而结束当前块的处理并且还可以通过debug来实现对于命中模块处理后文件的生成。 // CURRENT PLATFORM: GECKO// #IFDEF CHROMIUM // some expressions... // remove // #ENDIF// #IFDEF GECKO // some expressions... // retain // #ENDIF// #IFDEF CHROMIUM // some expressions... // remove // #IFDEF GECKO // some expressions... // remove // #ENDIF // #ENDIF// #IFDEF GECKO // some expressions... // retain // #IFDEF CHROMIUM // some expressions... // remove // #ENDIF // #ENDIF// #IFDEF CHROMIUM|GECKO // some expressions... // retain // #IFDEF GECKO // some expressions... // retain // #ENDIF // #ENDIF// 迭代时控制该行是否命中预处理条件 const platform (process.env[envKey] || ).toLowerCase(); let terser false; let revised false; let terserIndex -1; /** type {number[]} */ const stack []; const lines source.split(\n); const target lines.map((line, index) {// 去掉首尾的空白 去掉行首注释符号与空白符(可选)const code line.trim().replace(/^\/\/\s*/, );// 检查预处理指令起始 #IFDEF只会置trueif (/^#IFDEF/.test(code)) {stack.push(index);// 如果是true继续即可if (terser) return ;const match code.replace(#IFDEF, ).trim();const group match.split(|).map(item item.trim().toLowerCase());if (group.indexOf(platform) -1) {terser true;revised true;terserIndex index;}return ;}// 检查预处理指令结束 #IFDEF只会置falseif (/^#ENDIF$/.test(code)) {const index stack.pop();// 额外的#ENDIF忽略if (index undefined) return ;if (index terserIndex) {terser false;terserIndex -1;}return ;}// 如果命中预处理条件则擦除if (terser) return ;return line; });// 测试文件复写 if (debug revised) {// rm -rf ./**/*.logconsole.log(if-def-loader revise path, resourcePath);fs.writeFile(resourcePath .log, target.join(\n), () null); } // 返回处理结果 return target.join(\n);完整的代码可以参考https://github.com/WindrunnerMax/TKScript/blob/master/packages/force-copy/script/if-def/index.js并且有开发浏览器扩展v2/v3以及兼容Gecko/Chromeium相关的实现可以参考当然油猴插件相关的开发在仓库中也可以找到如果想使用已经开发好的loader的话可以直接安装if-def-processor并且参考https://github.com/WindrunnerMax/TKScript/blob/master/packages/force-copy/rspack.config.js配置即可。 每日一题 https://github.com/WindrunnerMax/EveryDay参考 https://juejin.cn/post/6945789317218304014 https://www.rspack.dev/config/builtins.html https://en.wikipedia.org/wiki/C_preprocessor https://webpack.js.org/plugins/define-plugin https://vitejs.dev/config/shared-options.html https://github.com/rollup/plugins/tree/master/packages/replace
http://www.pierceye.com/news/320749/

相关文章:

  • 徐州网站制作需要多少钱网站规划设计方案
  • 设计师常用网站门户重庆注册公司流程和费用标准
  • 网站图片太多怎么优化全民推广
  • 湖南做网站 e磐石网络做网站网站盈利会怎么样
  • 网站关闭流程保定风泉网络科技有限公司
  • 学做网站视频工作室网站需要备案吗
  • 个人网站 后台管理咸阳网站建设xymokj
  • 安阳淘宝网站建设保障性租赁住房管理平台
  • 建设银行网站最近都打不开吗在线设计网名生成器
  • 淮滨网站建设公司建设银行有招投标网站吗
  • 岳阳做公司网站可以做司法考试题的网站
  • 深圳做网站联雅asp.net网站很快吗
  • 网站制作公司交接网站网站建设 上海浦东
  • 甘肃省住房和建设厅网站移动网站登录入口
  • 垦利区建设局网站如何零基础学编程
  • wordpress金融小学生班级优化大师
  • 网站链接怎么做标记在哪个网做免费网站好
  • 山西响应式网站建设制作营销网站建设公司排名
  • 商学院网站建设建议深圳市宝安网站建设
  • 营销型网站建设报价方案中国建设银行舟山分行网站
  • 建游戏网站建筑工程公司管理制度
  • 网站风格配置怎么做wordpress下载弹窗插件
  • 合肥建设工会网站做试管网站
  • 商丘市有没有做网站建设工程检测预约网站
  • 网站产品内容在数据库wordpress都可以干什么
  • 宿州哪家做网站不做西安家电商城网站建设
  • 广安门外网站建设wordpress权限不能更新
  • 可以查企业备案的网站吗重庆建网站多少钱
  • 做网站如何分工中国十大企业
  • 网站开发和前端和数据媒体wordpress关闭主题