手机端网站怎么制作,wordpress jetpack,上海最专业的网站建设公司哪家好,网站打开慢这里给大家分享我在网上总结出来的一些知识#xff0c;希望对大家有所帮助 前言 每次用vite创建项目秒建好#xff0c;前几天用vue-cli创建了一个项目#xff0c;足足等了我一分钟#xff0c;那为什么用 vite 比 webpack 要快呢#xff0c;这篇文章带你梳理清楚它们的原理… 这里给大家分享我在网上总结出来的一些知识希望对大家有所帮助 前言 每次用vite创建项目秒建好前几天用vue-cli创建了一个项目足足等了我一分钟那为什么用 vite 比 webpack 要快呢这篇文章带你梳理清楚它们的原理及不同之处文章有一点长看完绝对有收获 正文 一、webpack基本使用 webpack 的出现主要是解决浏览器里的 javascript 没有一个很好的方式去引入其它的文件这个问题的。 话说肯定有小伙伴不记得 webpack 打包是咋使用的(清楚的话可以跳过这一小节)那么我以一个小 demo 来实现一下 1. 搭建基本目录结构 我们在vue项目中初始化后全局安装 webpack 和 webpack-cli yarn add webpack webpack-cli -g 创建vue所需的目录文件以及webpack配置文件。 目录结构如下 2. webpack.config.js配置文件编写 不清楚webpack配置项的朋友可以进官方文档瞅一眼webpack 中文文档 看完之后我们知道webpack主要包含的几个概念就开始编写配置文件了 (1)打包main.js 代码如下 const path require(path)
module.exports {mode: development, //设置开发模式entry: path.resolve(__dirname, ./src/main.js), //打包入口output: { //打包到哪里去path: path.resolve(__dirname, dist),filename: js/[name].js, //默认文件名main.js}
}为了方便我们运行我们去package.json中配置命令只需yarn dev就能运行了 dev: webpack server --progress --config ./webpack.config.js 运行后我们发现根目录多出了一个dist文件夹我们进到main.js中查看发现打包成功了 (2)打包index.html 问题❓我们知道vue项目中是有一个index.html文件的我们如果要打包这个html文件咋办呢 我们就需要借助plugin插件去扩展webpack的能力去装它 yarn add html-webpack-plugin -D 引入并使用它 const HtmlWebpackPlugin require(html-webpack-plugin)plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, index.html), //需要被打包的htmlfilename: index.html, //文件打包名title: 手动搭建vue //html传进去的变量}),]index.html 代码如下 !DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title% htmlWebpackPlugin.options.title%/title
/head
bodydiv idapp/div
/body
/html好啦我们再次运行打包命令发现dist目录下多出index.html文件打包成功 (3)打包vue文件 首先我们需要去安装vue的源码: yarn add vue 新建一个App.vue templatedivvue项目测试/div
/templatescript setup
/scriptstyle langcss scoped
/stylemain.js中写入 import { createApp } from vue
import App from ./App.vueconst app createApp(App)
app.mount(#app)我们再去打包发现报错了根据提示我们可以推断webpack是不能处理且不能编译.vue后缀的文件的这就需要引入loader及vue编译插件了装它 yarn add vue-loadernext yarn add vue-template-compiler -D 继续在配置文件中引入并使用 const { VueLoaderPlugin } require(vue-loader)module: {rules: [{test: /\.vue$/, //.vue后缀的文件use: [vue-loader] //启用vue-loader}]},plugins: [new VueLoaderPlugin()]再次打包打包成功我们可以测试一下用live server运行打包后的index.html看看会发现写在vue中的文字在页面成功展示 (4)打包css 那么我们如果要在vue中写css样式呢显然webpack是识别不了的还得loader来帮忙 yarn add css-loader style-loader -D 配置文件中加入新的一条css规则: module: {rules: [{test: /\.css$/, //.css后缀的文件use: [style-loader, css-loader]}]}去vue文件中把字体样式改为 红色后打包并测试一下成功 (5)配置babel 为了防止 webpack 识别不了高版本的 js 代码我们去装 babel yarn add babel/core babel/cdpreset-env babel-loader -D webpack.config.js 配置文件添加新的一条 js 规则 module: {rules: [{test: /\.js$/, //.js后缀的文件exclude: /node_modules/, //不包含node_modulesuse: [babel-loader]}]}babel.config.js 配置文件代码如下 module.exports{presets:[[babel/preset-env,{targets:{browsers:[last 2 versions]}}]]
}3. webpack热重载 热重载它是webpack的一个超级nice的插件让你不用每次都去执行打包命令装它 yarn add webpack-dev-server -D 之后我们去webpack.config.js中配置 devServer: { static:{directory: path.resolve(__dirname, ./dist)},port:8080, //端口hot: true, //自动打包host:localhost, open:true //自动跳到浏览器}此时还需要将package.json中的命令改改 dev: webpack server --progress --config ./webpack.config.js 我们使用yarn dev再次运行熟悉的一幕来了自动跳转到浏览器且将vue文件的内容展示在页面上修改vue内容也会自动打包 好了到这里基本的打包流程你已经get清楚了下面我们来研究研究它的打包原理吧 二、webpack打包原理 实现一个webpack的思路主要有三步 读取入口文件内容(使用 fs )分析入口文件递归的方式去读取模块所依赖的文件并且生成AST语法树 安装 babel/parser 转AST树)根据AST语法树生成浏览器可以运行的代码(遍历AST树) 安装 babel/traverse 做依赖收集安装 babel/core 和 babel/preset-env 让es6转es5 我们去新建一个目录结构如下(其中 add.js 和 minus.js 定义了两个值相加减的函数并将其抛出index.js中引入这两个函数并打印结果代码就不附上了比较简单) bundle.js 是我们用来打造 webpack 的文件代码如下 const fs require(fs);
const path require(path);
const parser require(babel/parser)
const traverse require(babel/traverse).default
const babel require(babel/core)const getModuleInfo (file) {//1 读文件const body fs.readFileSync(file, utf8); //读到路径下的文件内容//2 分析文件转AST树const ast parser.parse(body, { //body为需要解析的代码sourceType: module //以es6的模块化语法解析})// console.log(ast.program.body); //[{},{},{}...]//3 依赖收集 const deps {}traverse(ast, { //遍历astImportDeclaration({ node }) { //把import类型的对象找出来const dirname path.dirname(file) //拿到index.js所在文件夹路径const abspath ./ path.posix.join(dirname, node.source.value) //add.js文件的绝对路径deps[node.source.value] abspath //key:./add.js value:xxx/add.js}})//4.把ast-codeconst { code } babel.transformFromAst(ast, null, {presets: [babel/preset-env]})console.log(code);const moduleInfo { file, deps, code }return moduleInfo
}//5. 递归获取所有依赖
const parseModules (file) {const entry getModuleInfo(file)const temp [entry]const depsGraph {}for (let i 0; i temp.length; i) {const deps temp[i].deps //{ ./add.js: ./src/add.js, ./minus.js: ./src/minus.js }if (deps) {for (const key in deps) {if (deps.hasOwnProperty(key)) {temp.push(getModuleInfo(deps[key]))}}}}temp.forEach(moduleInfo {depsGraph[moduleInfo.file] {deps: moduleInfo.deps,code: moduleInfo.code}})// console.log(temp);return depsGraph
}//打包
const bundle (file) {const depsGraph JSON.stringify(parseModules(file));//手写一个require 借助evalreturn (function(grash) {function require(file) {function absRequire(relPath) {return require(grash[file].deps[relPath])}var exports {};(function(require, code) {eval(code)})(absRequire, grash[file].code)return exports}require(${file})})(${depsGraph})}const resultbundle(./src/index.js)
fs.mkdirSync(./dist)
fs.writeFileSync(./dist/bundle.js, result)我们使用node去运行这个文件去到 html 页面上发现控制台能输出加减法对应的结果说明打包成功 但是webpack有一个缺点如果在这个文件中需要改动一点点再保存webpack的热重载又会重新自动打包一次这对于大型项目是极不友好的这时间估计等的花都要谢了。那么vite出现了 三、vite打包原理 我们知道当声明一个 script 标签类型为 module 时浏览器会对其内部的 import 引用发起 HTTP 请求获取模块内容。那么vite 会劫持这些请求并进行相应处理。因为浏览器只会对用到的模块发送http请求所以vite不用对项目中所有文件都打包而是按需加载大大减少了AST树的生成和代码转换降低服务启动的时间和项目复杂度的耦合提升了开发者的体验。 1. 需要解决的问题 那么要打包一个vue项目它的入口文件是main.js浏览器会遇到三个问题 import { createApp } from vue //浏览器无法识别vue路径
import App from ./App.vue //浏览器无法解析.vue文件
import ./index.css //index.css不是一个合法的js文件因为import只能引入js文件const app createApp(App)
app.mount(#app)知道怎么解决这几个问题我们就能打造一个vite了 2. 打造vite 我们使用 koa 去搭建一个本地服务让其可以运行新建一个 server.js 文件用来打造 vite 代码如下 //用node启一个服务
const Koa require(koa);
const app new Koa()
const fs require(fs)
const path require(path)
const compilerDom require(vue/compiler-dom) //引入vue源码 能识别template中的代码
const compilerSfc require(vue/compiler-sfc) // 能识别script中的代码function rewriteImport(content) {return content.replace(/ from [|]([^])[|]/g, (s0, s1) {//若以 ./ ../ / 开头的相对路径console.log(s0, s1);if (s1[0] ! . s1[0] ! /) { //vuereturn from /modules/${s1} //去http://localhost:5173/modules/vue} else {return s0}})
}app.use((ctx) {const { request: { url, query } } ctxif (url /) {//读index.htmlctx.type text/html //设置类型let content fs.readFileSync(./index.html, utf8) //读文件// console.log(content);ctx.body content//content输出给前端}else if (url.endsWith(.js)) { //js文件 /src/main.jsconst p path.resolve(__dirname, url.slice(1)) // src/main.js 拿到文件的绝对路径ctx.type application/javascriptconst content fs.readFileSync(p, utf8)ctx.body rewriteImport(content)}else if (url.startsWith(/modules)) { // /modules/vueconst prefix path.resolve(__dirname, node_modules, url.replace(/modules/, )) // vueconst module require(prefix /package.json).module //读取package.json中的module字段 拿到vue的模块源码地址const p path.resolve(prefix, module) // 拿到vue的模块源码的终极地址const ret fs.readFileSync(p, utf8) //读取文件ctx.type application/javascriptctx.body rewriteImport(ret) //递归 防止vue源码又用到了其它模块}else if (url.indexOf(.vue) -1) {const p path.resolve(__dirname, url.split(?)[0].slice(1)) // src/App.vueconst { descriptor } compilerSfc.parse(fs.readFileSync(p, utf8))console.log(descriptor);if (!query.type) { // 返回.vue文件的js部分ctx.type application/javascriptctx.body ${rewriteImport(descriptor.script.content.replace(export default , const __script ))}import { render as __render } from ${url}?typetemplate__script.render __renderexport default __script} else if (query.type template) { // 返回.vue文件的html部分const template descriptor.templateconst render compilerDom.compile(template.content, {mode: module}).codectx.type application/javascriptctx.body rewriteImport(render)}}else if (url.endsWith(.css)) {const p path.resolve(__dirname, url.slice(1))const file fs.readFileSync(p, utf8)const content const css${file.replace(/\n/g, )}let linkdocument.createElement(style)link.setAttribute(type,text/css)document.head.appendChild(link)link.innerHTML cssexport default cssctx.type application/javascriptctx.body content}
})app.listen(5173, () {console.log(listening on port 5173);
})3. vite热更新 那么vite 的热更新怎么实现呢 我们可以使用chokidar库来监听某个文件夹的变更只要监听到有文件变更就用websocket通知浏览器重新发一个请求浏览器就会在代码每次变更之后立刻重新请求这份资源。 (1) 安装chokidar库 yarn add chokidar -D (2) 之后去新建一个文件夹chokidar在其中新建 handleHMRUpdate.js 用于实现监听 const chokidar require(chokidar);export function watch() {const watcher chokidar.watch(../src, {ignored: [**/node_modules/**, **/.git/**], //不监听哪些文件ignorePermissionErrors: true,disableGlobbing: true})return watcher
}export function handleHMRupdate(opts) { //创建websocket连接 客户端不给服务端发请求服务端可以通过websocket来发数据const { file, ws } optsconst shortFile getShortName(file, appRoot)const timestamp Date.now()let updates;if (shortFile.endsWith(.css)) { //css文件的热更新updates [{type: js-update,timestamp,path: ${shortFile},acceptPath: ${shortFile}}]}ws.send({type: update,updates})
}本文转载于: https://juejin.cn/post/7267791228872671247 如果对您有所帮助欢迎您点个关注我会定时更新技术文档大家一起讨论学习一起进步。