从手工作坊到自动化生产

前端工程化初步认识

Posted by 顾小五 on August 16, 2018

小白前端摸索记(一)—— 从手工作坊到自动化生产

本文需知,渣小白,可能会有很多错误,希望指出,亲身踩坑经历总结

本文内容,关于前端自动化的基本理解以及从零开始构建一个自动化项目

学习前端也半年有余吧,在网上的前端学习初级教程中,是学习html/css/js的基本使用,然后进阶开始学习各种打包工具啊什么的,感觉这个过程非常困难,因为突然很多的概念,感觉有种很乱很乱的理不清的感觉。(笑)这篇文章就是我的理解吧。(可能有很多错误,非常希望有人能指出QUQ(再次强调

前端初级学习

最开始学习前端的时候,其目录结构是这样的↓↓↓

|-------index.html
|-------css
		|------index.css
		|------xxx.css
|-------js
		|------index.js
		|------xxx.js

嗯,没什么毛病。我也这么一直度过了半年

其实这对前端的初学者来说感觉没毛病,因为一开始做小demo的话,这样也的确能够搞定。

前端自动化工具

在上面的一个阶段呢,写js基本上就是一个文件从头写到尾,css文件也是,然后要共用的部分甚至不是抽出一个单独的部分而是直接复制粘贴。这样就有很多的问题,写码一时爽,维护。。。。算了这种等级的demo根本不需要维护(笑)

但是当做的网站越来越复杂的时候,这样的结构来说过于简单,不利于大型项目的模块开发,以及后续的维护工作,在较大的项目中,尤其是多人的情况下,如何组织多个模块,这就成了一个问题(在初级学习的时候甚至连import都不需要,当然不需要的什么组织啊结果啊之类的 ~ ),怎么说呢,当有需求的时候就会去寻找新的工具,来满足需求。

所以,当当当~这种情况下就需要gulp,grunt,webpack 等等自动化构建工具,这类工具可以管理项目的各类文件之间的关系,解决之间项目引用的问题。当然也不止这些,还有上线的时候做的一些压缩,uglyfy,然后各种吧!总之这些工具,基本上是可以解决从开发到上线等等一系列问题。

这个时候目录结构就变成了这样 ↓↓↓

|-------dist			//构建完了之后用来上线的目录
    |-------js
            |------index.bundle.js
    |-------images
    	    |------xxx.jpg
|-------src				//生产目录
	|-------script
	|-------style
|-------webpack.config.js
|-------package.json
安装以及核心概念
  1. 首先初始化文件夹

    npm init
    

然后就会有这么多东西要填,分别是填对应这个工程的名字(package name),版本(version),描述(description),入口文件(entry point),test命令(test command),关键字(keywords),然后作者(author),最后是模块协议(license)

1538054962519

这一步呢,就是初始化文件夹,当然像我这样全部默认的话完全可以

   npm init -y

这样可以少敲不少回车

这里很多都可以从字面上理解,主要要解释的

  1. 这样会生成一个package.json文件

    这个是最最最最最最重要的一个文件之一,这个是一个项目的配!置!文件,好了知道配置文件就知道重要性了(笑)

  2. main

    这个是这个项目的默认入口文件

  3. script

    这个是用于快捷输入命令行的,比如说在构建的时候我们经常会带上一大堆参数,我们可以在script里面写上,然后下次直接npm run + 你给这条命令取的名字 就好啦~

  4. devDependencies 和 dependencies

    这两个属性呢,在初始化的时候还没使用到,这个在之后安装模块的时候,是使用到这个的

    其实最直观的差别就是

    npm install xxxx --save-dev
    npm install xxxx --save
    

    参数不一样

    dependencies 呢就是程序运行的时候所必须的模块(emmmm 类似引用jQuery那种?)

    devDependencies 呢就是程序在开发的时候用到的一些辅助工具啊,比如babel,webpack之类的,这种只要在开发的时候用,发布的时候并不需要的发布的就放在jQuery中

  5. 大致要用到的就这些,其他用到的后续会补充,以及想要详细了解,传送门 ↓

    https://docs.npmjs.com/files/package.json

  6. 这里选择webpack啦2333

    1. 既然用webpack,那么第一步就是安装webpack啦

      npm install webpack -g
      

      咦,为什么是 -g ?webpack 用之前要全局安装一下,不然之后运行webpack命令的时候会报错= =!

      安装之后在终端输入 webpack 还是会报错,阅读报错信息他就会提示你安装一个关于 webpack的命令的包,所以呢可以直接根据提示安装或者直接

      npm install  webpack-cli -g
      
    2. 通过第一步,就有webpack这个工具在,那么怎么使用呢?

      当然,还是主要通过配置文件——webpack.config.js,不知道这个会不会自己生成,不会的话那就自己创建一个,都一样的啦23333

      webpack.config.js 这个文件,看后缀名就知道是个js文件(废话

      它还是遵守commonJs的规范,所以,他的主要结构还是

      module.exports = {
          //...各种配置
      }
      
    3. 4个核心概念

      • entry

        简单来说就是打包的时候的文件入口

      • output

        简单来说就是打包的时候的文件出口

      • loader

        这个概念没有前面两个那么明显,查看webpack文档,它是这么介绍loader的:→传送门1534399006480

        翻译一下就是:

        webpack只能解析js文件,通过loaders , webpack可以打包其他文件的,然后将它转化成module然后他就可以 被你的应用使用了。

        并且注意可以处理任何类型(类似.css)文件是webpack的一个特有能力,可能不支持于其他的工具,我们认为一个语言的拓展性是非常必要的,这样可以方便开发者创建更加准确的依赖图

        loader含有两个的属性需要在webpack中配置:

        1. test 匹配规则,匹配哪种类型的文件
        2. use 匹配成功之后用哪种的loader处理
      • plugin

        plugin,这个还是看文档吧,官方文档其实也说的很清楚了→传送门

        1534400264904

        翻译一下呢,就是loader被用来转化各种类型文件,plugin则可以完成更大范围的任务,就像打包的优化,资源管理和更改环境变量

        然后就是怎么使用plugin,你需要require它们然后将它加入plugins的数组里面,大多数plugin都是可以通过选项配置的。你可以使用一个plugin在同一个配置文件中多次使用,这时你需要使用new操作符来实例化它。

具体配置

核心概念介绍完开始,那么就可以开始配置了

  1. 既然要开始打包,就要设置入口文件和出口文件

    {
        entry: "./src/index.js",
        output:{
            path:path.resolve(__dirname, 'dist'), 
            filename: "js/[name].bundle.js"
        },
    }
    
    • 出口和入口可以凭借英文很直观的了解~

    • 这个只是最基本的写法啦23333

    • 当然还有多种写法

    • 关于entry

      刚刚上面的结构其实是这样的

      1538055118682

      这个是一个文件入口对不对,那么我们想要多个文件入口怎么办呢,比如从一个文件进入,并不能将我所需要的文件全部打包进来,就像下图,它存在平行关系,这种时候要怎么办呢,

      1538055163311

      来,继续打开官方文档→传送门

    1538055182821

    咦,single entry?单入口?好像跟我们想的不一样?

    看图片最下方的蓝色小字,当你给entry传入一个数组会发生什么呢?通过传入一个数组的到entry创建多入口文件,这当你想在给多个独立的(就是没有相互依赖关系的)文件打包到同一个chunk下非常有用。

    所以想实现多入口文件的话,只要传入一个数组就行啦!上面那张图就可以按照以下的写法写就行了。

    {
        entry: ["./src/index.js","./src/c.js"],
        output:{
            path:path.resolve(__dirname, 'dist'), 
            filename: "js/[name].bundle.js"
        },
    }
    

    嗯,刚刚又多了一个新名词,chunk。

    什么是chunk呢,我的个人理解就是,webpack给每个依赖关系划分为一个chunk

    刚刚之前那个数组就是一个chunk,我们这种写法的就不明显了,我们换个写法

    {
        entry: {
            main:["./src/index.js","./src/c.js"],
            d:["./src/d.js"]
        },
        output:{
            path:path.resolve(__dirname, 'dist'), 
            filename: "js/[name].bundle.js"
        },
    }
    

    这样写就明显多了,刚刚那个数组其实就是对应了一个叫做main的chunk,并且可以有多对chunk,chunk有什么用呢?这个在定义output就有用啦!

  2. output

    首先查看官方文档→传送门

    1538055206468

    filename是对应的输出文件的名字,如果就像上面的,官方文档的例子是一直定义成了”bundle.js”,我前面的例子使用到了一个”[name]”关键字,这个呢是一个关键字吧,就是对应的chunk的名字,然后这样就可以直接生成对应的bundle.js然后其实还有一些其他的的关键字,比较常用的[chunkhash ],[hash]

    在官方文档上没有找到这个两个词的解释,在这里找到一个两年前的官方解释。感觉这东西也没什么道理会变就引进来了

    1538055226725

    [hash]就是这次编译的hash,[chunkhash]就是这个chunk的hash

    这么理解吧,[hash]就是只要编译了,那么一定就会变(因为是和编译挂钩的),chunkhash呢是看这次编译之后这个chunk生成的文件内容变化了没有,变了的话[chunkhash]就会变

  3. 出入口讲完了,现在该loader了

    官方文档走一波→传送门

    前面一大段是讲loader的作用的,我们看完之后可以开始看样例:

    1538055243255

    第一步当然是安装loader啦~

    就像前面entry,output,loader对应的是module哟,

    然后就有rules来匹配,这个照着官方文档来就行

    就像前面介绍的时候一样,每个rule下面的要有test和use

    其实看着官方文档和前面的介绍就很简单啦2333

    然而并不是只有这么简单

    {
    	test:/\.css$/,
        loader:'style-loader!css-loader'
    }
    

    咦好像和上面的有一点不一样,

    来,我们首先来理解一下这两个loader是什么鬼,第一个cssloader第二个是处理styleloader(解析是从后往左)styleloader是从可以讲css样式插入到html文档和head标签里面的,这就是两个loader串在一起

    当然还可以三个

    {
    	test:/\.css$/,
        loader:'style-loader!css-loader?importLoaders=1!postcss-loader'
    }
    

    咦道理我都懂,再加一个参数加一个!+新loader不就好了吗?importLoaders=1这个又是什么

    这里呢我们用到postcss-loader的一个插件叫做autoprefixer ,给自动增加浏览器前缀,其实不加?importLoaders=1也是可以用的,但是还是会有一个问题,当在css文件中使用@import 文件这时候import进来的文件就不会加浏览器前缀,这个时候就通过传参(类似url中的get传参)importLoaders参数呢就是用来指定css中import进来的资源的通过几个loader,因为后面只有postcssloader所以importLoaders=1

    关于postcss-loader的配置我就不多说了,具体配置方法可以查看官方文档→传送门

    其他的也都大同小异,我就直接放我之前配置的

        module:{
            rules:[
                   {
                       test: /\.js$/,
                       exclude: path.resolve(__dirname, "node_modules"),
                       include: path.resolve(__dirname,"src"),
                       loader: "babel-loader",
                       query:{  //babel参数配置
                           "presets": ["env","react"]
                       }
                   },
                   {
                       test:/\.css$/,
                       loader:'style-loader!css-loader?importLoaders=1!postcss-loader'
                   },
                   {
                       test:/\.scss$/,
                       loader: 'style-loader!css-loader!postcss-loader!sass-loader'
                   },
                   {
                       test:/\.html$/,
                       loader:'html-loader'
                   },
                   {
                       test:/\.(jpg|png|gif|svg|mp3|ico)$/i,
                       loader:'url-loader',
                       query:{
                           limit:20000,
                           name:'[name]-[hash:5].[ext]',
                           outputPath: 'images/'
                       }
                   }
            ]
        },
    

    其中比较特殊的就是

    1. babel-loader

      这个loader的配置方法可以直接http://babeljs.io/setup然后选择webpack就有详细的教程~

    2. sass-loader

      sass-loader还依赖与一个node-sass,下个包会被墙,可以尝试淘宝源

      npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
      
    3. url-loader/file-loader

      这两个是一起使用的,

      file-loader:返回的是图片的public URL。

      url-loader:与file-loader不同,url-loader可以在图片大小小于设定的limit的时候返回的是一个DataURL(base64码),大于limit时会调用file-loader对图片进行处理。

  4. 最后就是plugin了,这个我用的也不多我就用了一个html-webpack-plugin orz

    那,plugin要怎么用呢,我就拿html-webpack-plugin举例吧(放弃webpack官方文档

    那。我们就打开html-webpack-plugin的文档→传送门

    第一步当然是安装

     npm i --save-dev html-webpack-plugin
    

    就像前面介绍的,webpack中怎么使用plugin,require,然后在plugin数组中添加,所以看文档的时候也可用知道

    1538055278768

    打包结果

    1538055295860

    就这么简单((((

    显然不可能

    为了应对多种状况,plugin当然是可以配置的

    比如html-webpack-plugin

        plugins:[     
            new htmlwebpackplugin({
                filename: 'index.html',
                template: './src/index.html',
                chunks: ['index'],
                favicon:'./src/component/20180806050252871.ico',
            }),
        ],
    

    在实例化plugin的时候可以传入一个对象,然后对其进行配置

    filename是生成的html名字,template是模板html的路径

    chunks,这个就是生成html中引入的js的chunk的名字,还记得大明湖畔之前提到的chunk的作用吗,你看这里就可以很好的体现chunk的作用了,favicon是icon的配置,还有更多配置参数可以从上面我给的官方文档自己查阅

小结

终于写完了(╯‵□′)╯︵┻━┻

一写写了这么多

嗯,而且还只是个初步配置,其实我后面配置的不止这些,还是那句话吧,有需求时才会去寻求工具去解决。这个过程可能没有那么简单XDDD但是解决了真的很方便。

最后多查文档吧!文档大多数都给出了很多的解决方案,顺带webpack的文档我看的有种云里雾里的感觉。。。。