( 开篇 )仿写'Vue生态'系列___'你webpack溜么?'

( 开篇 )仿写'Vue生态'系列___'你webpack溜么?'

关于这个系列 css

做者离职深造也有一个月了, 前端相关的视频与资料学了很是多, 本身感受到如今的知识之间只是呈现出一种相互之间的弱联系, 也就是还不成'体系', 每个知识点我都学过我都会用, 可是统一块儿来就有些地方不是很明朗了,相信不少前端仔也会有一样的感觉.前端

好比说:
1: 你知道你常常用的插件的原理么? 🤔
2: 你能写出一个与他功能相仿的插件给别人去使用么? 🤷‍♀️
3: 不借助插件你能正常开发么? 🕊
4: 面试时候面试官问你,某些技术的原理, 你是否只能回答"我只是站在巨人的肩膀上?"┑( ̄Д  ̄)┍vue

为了把这些'围绕着框架'展开的一系列知识弄懂, 因此本次与你们一块儿从0开始, 写一套简易版的vue框架, 不但要写框架, 咱们还要写 vuex, vue-router, axios, 简易的webpack, webpack-loader, webpack-plugin等等, 涉及到的知识点都会拿出来详谈一番, 若是有哪里没有讨论到位的咱们能够私信继续死磕, 本次项目会在github上同步更新 githubnode

本人去年至今年年初在公司一直负责面试前端, 因此文章中不但涉及技术, 中间还会穿插着个人理解与以前作过的笔记, 以及面试时候我遇到的一些问题, 毕竟再过一个月或者两个月我也就结束'闭关'去找工做了, 因此对面试这方面也会有所讨论, 相关知识与问题 你们均可以一块儿交流, 共同窗习,共同进步, 早日实现自我价值!!react

一. 前端与webpack

(本次工程师基于webpack4)
webpack如今已经能够说是前端必会的技能了, 若是有人跟你说, 那东西看看文档就行不用学, 那他就是在害你, webpack是一门须要手熟的技术, 最基本的结构你必须不看资料能纯手打出来, 如今框架都帮咱们集成好了开发环境, 从另外一方面来讲也会温水煮青蛙麻痹咱们的神经, '只要会用就能够了'并不适合webpack, 这门技术能够帮咱们更好的了解前端这个职位的定义, 工欲善其事必先利其器, 那么本期就从webpack的配置开始吧!webpack

需求分析
1: 本次实现的简易框架 暂定名字为 'C', 开发使用class的形式, 因此须要支持es6
2: 能够加载css文件, 由于有了样式, 写代码才有乐趣
3: 热更新
4: 区分生产环境与开发环境, 就是为了巩固webpack优化的相关知识ios

基础的搭建--逐行解析!

1: 建立一个dist文件夹存放打包文件
2: 建立一个src文件夹存放开发信息
3: src里面index.js 主要的导出文件
4: 安装依赖 npm install webpack webpack-cli -Dgit

下面这段最基础的代码你能不看资料本身手敲么
标题的'溜'不是指多么的深刻, 而是能把最基础的东西准确的作出来.es6

const path = require('path');
module.exports = {
   mode:'development',
   entry:{
    main:'./src/index.js'
   },
   output:{
     filename: '[name].js',
     path: path.resolve(__dirname, '../dist')
   }
}

知识点汇总( 死磕知识点... )github

一: path

  1. node的原生自带模块.
  2. 与其一同出现的有 resolve join 两个方法
    join:
    windows下文件路径分隔符使用的是"", Linux下文件路径分隔符使用的是"/", path.join() 方法使用平台特定的分隔符把所有给定的 path 片断链接到一块儿,并规范化生成的路径。长度为零的 path 片断会被忽略。 若是链接后的路径字符串是一个长度为零的字符串,则返回 '.',表示当前工做目录.
    onsole.log(path.join('a','b','c','..','d')) 打印 a/b/d 被拼接为一体, ..把c干掉了;
    resolve:
    方法会把一个路径或路径片断的序列解析为一个绝对路径, 若是没有传入 path 片断,则 path.resolve() 会返回当前工做目录的'绝对路径'。
    console.log(path.resolve('a','b','c')) /Users/lulu/web/cc_vue/a/b/c 精确到了工程

二: module.exports
node的模块导出方式, 为何他是用'.'连接, 而咱们使用的es6 是' '空格,好比:export default
缘由: 学过node的朋友会很好理解, node能够随时的读取文件, 而module只是个变量而已, exports是module上面的一个属性, 每次读取文件以后, 用户想要输出的文件挂载到这个对象上行了, 因此它使用'.'的这种方式, 实则是在操做对象. 而es6的语法在浏览器上是实现不了的, 须要编译以后再执行, 它采用的是关键字模式, 好比我来编写一个打包插件来打包export default这种语法, 我须要先解析出语法树, 而后把它的文件绝对路径转换为key, 内容是value的一个闭包(这方面后面会作一下), 因此es6的引入方式才是同步, 没法随处的写;

三: mode:webpack新增的模式属性

第一. none 不使用webpack自带的模式
第二. development : 开发模式

1. 不压缩
 2. 不摇树(为何开发环境不'摇树'? 缘由是若是去除了多余的代码, 那么调试的时候就找不到准确的段落了;)
 3. 有完善的错误提示
 4. 能够设置完备的source-map
// 添加了下面的两句
plugins: [
   // 当开启 HMR 的时候使用该插件会显示模块的相对路径,而不是文件的id。
   new webpack.NamedModulesPlugin(),
   // 更改全局的环境变量
   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
      ]

第三. production 线上模式

// 自动添加
   plugins: [
   // 压缩js代码
    new UglifyJsPlugin(/* ... */),
   // 设定环境变量
    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
   // 过去 webpack 打包时的一个取舍是将 bundle 中各个模块单独打包成闭包。这些打包函数使你的 JavaScript 在浏览器中处理的更慢。相比之下,这个插件 能够提高或者预编译全部模块到一个闭包中,提高你的代码在浏览器中的执行速度。
    new webpack.optimize.ModuleConcatenationPlugin(),
// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样能够确保输出资源不会包含错误。对于全部资源,统计资料(stat)的 emitted 标识都是 false。
    new webpack.NoEmitOnErrorsPlugin()
]

四: entry

  1. 直接写字符串 entry:./xx/index 此时默认名为main
  2. 多入口文件写数组[./xx1/index, ./xx1/index]
  3. 对象, 也是官方推荐的写法, 每一个入口都定义名字, 配合打包输出使用
entry :{
  main:'./src/index.js',
  beas:'./src/base.js'
}

五: output 这个比较单一

// 这里能够自定义名字, 就是'写死'就行
// 下面这种方式意思就是 上面entry的名字是啥, 这里就是啥
// 通常多入口打包都这么写
  filename: '[name].js',
// 这个形式结合上面的知识点确定都没问题了, 
// __dirname: 当前文件所在文件夹
  path: path.resolve(__dirname, '../dist')

看了这些感觉如何??? 虽就这么几行代码, 但也要死磕一下

babel相关

npm install babel-loader -D
module里面的rules选项是专门玩loader的
只有一个key, 还写成对象形式, 估计之后会有所动做

module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader?cacheDirectory=true'
      }
    ]
  }
  1. test 也就是指定的匹配规则, 后面有机会的话 我讲一下正则相关的技巧
  2. exclude 不处理的文件,也就是说依赖所有不翻译, 不加这句会很慢的
  3. loader也就是具体用哪一种方式处理匹配出来的文件内容, 上面这种写法是官网推荐的写法, 默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。以后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 从新编译过程

babel-loader干么的?
看过他的源码才知道, 他是个领导, 负责安排专门的人来执行编译工做, 而他本身只负责调度与输出.

核心员工"@babel/core"
npm install @babel/core -D
他的命名式7.x 开始规范下来的, 有的人可能见过'-'连接的, 都是早期的版本了, 由于他只是一个执行具体动做的模块而已, 因此须要@babel开头
实际上, 具体的工做都是这个模块去完成的, 因此你只加载babel-loader是没有用的.
其实babel团队这样设计才是符合设计模式的, webpack也分出 webpack-cli, 如今不少插件都不是单一一个文件了.

.babelrc
webpack的配置项实在是太多了, 若是都集中在一个文件里面就不符合设计原则了, 像这种很经常使用, 并且配置项比较多的, 会被单独抽离出来配置, babelrc(和.babelrc.js/ package.json#babel)文件名 均可以

@babel/preset-env 指定目标的需求🤔
npm install @babel/preset-env -D
babel转换js代码的功能其实能够很细化, 好比说有的人须要所有转为能够兼容 ie7的语法, 而我平时转换为 Chrome 最近5个版本 能兼容的就能够, 因此不必转换太多致使性能占用,

就要在.babelrc这个文件里面 设置在 presets选项下
指定我到底要解析到什么地步
他有一个对照表, 把配置项合并计算出交叉配置

"presets": [
    [
      "@babel/preset-env",
      {
        // 写法1
        "targets": {
            // 用户占比大于1%的浏览器的 最后两个版本
          "browsers": ["> 1%", "last 2 versions"]
        }
        // 写法2
        "targets": {
          "chrome": "58",
          "ie": "11"
         }
      }
    ]
  ],

下面这种形式: 若是不进行任何配置, preset 所包含的插件将支持全部最新的 JavaScript (ES201五、ES2016 等)特性。

"presets": ["@babel/preset-env"],

官方维护的类型
@babel/preset-env
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript
Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
Stage 1 - 建议(Proposal):这是值得跟进的。
Stage 2 - 草案(Draft):初始规范。
Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
preset-es2015 等等 被@babel/preset-env取代, babel7版本

上面咱们只是作到了好比说 箭头函数这种转换, 可是像promise这种实例方法是没有的, babel分工很明确, 上面的配置只作这方面的转换

霸道的polyfill
运行时的库, 因此要是save!!!!
npm install --save @babel/polyfill
这个库将会模拟一个彻底的 ES2015+ 的环境。
使用就是取对应的页面上require他
为何说他霸道?
这家伙就是个铁憨憨, 一股脑的把全部配置都注入进来, 有了它, 打包后的体积大的吓人, 但他也是最全面的, 致使我没用到的功能他都给我写好了, 那就很是没有必要了, 因此babel给了配置项, 能够只注入咱们用到了的类与方法, 这样体积会减少不少.
仍是要交给'需求'去作.

"presets": [
   ["@babel/preset-env",{
        "useBuiltIns":"usage"  // 使用polyfill了时候只加载使用到的方法
    }]
 ]

他还有一个致命的缺点, 就是污染全局, 好比说我开发一个插件给你用, 我就把你的全局变量都改为最新版的了, 开发插件的准则就是尽可能不要动用户的数据, 万一用户也引入了另外一个版本的他, 那就太可怕了, 因此开发插件babel提供了下面的方案

@babel/plugin-transform-runtime
plugin-transform-runtime 已经默认包括了 @babel/polyfill,所以不用在独立引入。
npm install @babel/plugin-transform-runtime -D
他也算是个分配至, 须要安装一个干活的
具体代码注入都是在他文件里面取出来的
npm install --save @babel/runtime

避免屡次编译出helper函数:
Babel转移后的代码想要实现和原来代码同样的功能须要借助一些帮助函数
@babel/runtime包就声明了全部须要用到的帮助函数,而@babel/plugin-transform-runtime的做用就是将全部须要helper函数的文件,依赖@babel/runtime包

不会污染全局变量
屡次使用只会打包一次
依赖统一按需引入,无重复引入,无多余引入

// 缺点
不支持实例化的方法Array.includes(x) 就不能转化, 由于若是实现了这个就是污染全局了啊
若是使用的API用的次数不是不少,那么transform-runtime 引入polyfill的包会比不是transform-runtime 时大

这个要在.babelrc文件夹,plugins选项里面配置

"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        // 若是配置他为2, false不用
        // 需安装 @babel/runtime-corejs2
        // 则不用安装 @babel/runtime
        // 做用: promise 代码变成了一个独立的变量 _promise,不会影响全局的 Promise。
        "corejs": 2,
        // 默认就是true  写出来让你们知晓
        "regenerator": true, // 切换生成器函数是否转换为使用不会污染全局范围的再生器运行时。
      }
    ]
  ]

上面的配置好了, 就能够正常玩耍es6了, 若是上面的你全都看完了, 那你还怕之后别人问你这方面知识么??

css样式方面

开篇也说了, 有个好的造型, 代码写起来也愉悦

module: { // 只有一个key, 还写成value, 估计之后会有所动做

    rules: [
      {
        test: /.(css|sass|scss|less)$/,
        use: ['style-loader', 'css-loader', 'postcss-loader']
      }
    ]
  }

配置大同小异, 我来具体解释下每个loader

  1. style-loader: 使用<style>标签将css-loader内部样式注入到咱们的HTML页面
  2. css-loader: 主要用于处理图片路径 与 导入css文件的路径 而后义字符串的形式存入js
  3. postcss-loader: 为css样式加上必要的前缀
    他须要配合autoprefixer使用, npm install -autoprefixerD, 颇有必要的优秀软件
    与.babelrc相似的配置文件以下, 名称为
// 他须要导出一下, 不想.babelrc直接读取{}内的
module.exports = {
  plugins: [
    require('autoprefixer')({
        // 1: 最新版的话 browsers改为overrideBrowserslist就不报错了
        // 2: 部分用户出现不写[]里面的内容就不编译的bug
      overrideBrowserslist: [
          'iOS 7.1', 
          'ff > 31', 
          'ie >= 8',
          'Chrome > 31', 
          'Android 4.1', 
        ]
    })
  ]
};

遇到的知识都作了梳理, 工欲善其事必先利其器, 每个知识点都值得探究, 这边写的多了电脑就好卡, 下篇把剩下的几个知识点说一下就能够正式作框架了, 写文章的过程也是对本身的一种审视, 把不少问题拿到面前质问本身到底会不会, 挺累的但也是收获满满.

end
你们均可以一块儿交流, 共同窗习,共同进步, 早日实现自我价值!!

github: github
我的技术博客: 我的技术博客

相关文章
相关标签/搜索