npm&node.js

前言:【一】什么是node.js?( 参考网址https://blog.csdn.net/hu_zhiting/article/details/51450694)

                     node.js是JavaScript的运行环境。这句话怎么理解呢? js是一个脚本语言,需要一个解析器才能运行。对于我们前端开发写在html中的JavaScript,浏览器就充当着解析器的角色。 而需要独立运行的js,node.js就是一个解析器。即node.js可以让JavaScript运行在服务器端的开发平台,这使得JavaScript不再受限于前端网页的开发,也可以进行后端服务程序的开发。[每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许JS使用运行环境提供的内置对象和方法做一些事情。例如运行在浏览器中的JS的用途是操作DOM,浏览器就提供了document等内置对象]那么,运行在no de.js的JavaScript的用途是操作磁盘文件或搭建HTTP服务器,Node.js就相应提供了fn和http等内置对象。]即:Nodejs不是语言,语言是JavaScript,Nodejs是一个平台,让我们的JS可以运行在服务端的平台。接下来,我们只关注JavaScript在前端中的使用,不关注javascript的后端开发。注意:node.js可以执行JavaScript,指的是可以执行JavaScript的核心代码,但是不能执行和浏览器相关的代码,例如:执行document.getelementbyid("test")就报错。例如:

                     1.我们安装了nodejs之后,nodejs执行器就被安装在系统中。即可以执行任何文件中的JavaScript代码

                     2.例如我们创建一个main.js ,不用浏览器执行,这里用node执行:在命令中找到该文件对应的路径,然后,输入命令:node main.js                                                

                          在终端,就可以输出20和50

                          

                         3.在main.js中打印和浏览器相关的内容,则报错:                                       

                        

 

         【二】为什么要使用NodeJS 

                    NodeJS的特点: 

                   1. node.js基于JavaScript语言,不需要再学习一门新的语言。JavaScript语言在Web前端开发中至关重要,所以,前后台统一语言,可以实现程序员全栈开发,统一公共类库,代码标准化。

                  2. NodeJS选择了目前最快的浏览器内核V8作为执行引擎,保证了其性能和稳定性。
                  3. NodeJS开发高效,代码简单。异步编程是其另一特点,让其处理IO密集型应用有明显优势。
                  4. NodeJS的社区在壮大,不仅包的数量在快速增加,而且质量也明显好于其他语言。(n p m)

                    对于前端而言,nodejs的必要性:

                   1.nodejs可以执行javascript代码;(JavaScript核心代码)
                    2.node.js可以执行模块化,例如require exports  inmport等,方便开发。但是这又有一个问题,浏览器并没有模块化这个概念即:require 、exports import等并不能被浏览器所识别(执行就报错),即在一般的项目中,都会有一些浏览器不能识别的HTML或者css或者js,比如使用elementUI或者.less或者es6中的一些语法或者.ts等文件(开发插件对外提供的.js文件也是通过打包把各个文件把包成一个js文件,提供给外部使用),但是项目中我们经常需要用到这些,这时候就需要用到打包工具(如webpack)将项目编译转换成浏览器所能识别的语言或者语法即经过打包之后,js、css都会是合并后的文件也就是能被浏览器所识别的、普通的css和javascript,从而浏览器可以正常加载执行javascript【打包后的JavaScript就是正常的、普通的、能被浏览器所识别的JavaScript代码。因为打包工具如webpage是纯js文件,打包的过程就是执行js代码的过程,因此该webpage需要一个运行环境,而nodejs刚好可以运行js文件,因此,我们在项目中需要首先安装nodeJS。

                 3.nodejs一起安装的包管理器npm方便我们下载开发工具和开发库。例如npm install vue等。

               即:

  • 打包工具是 CSS/JS 的发布时的合并,因为你写的时候,特别是应用了各种预处理器以及衍生语言等写 CSS/JS 的时候,源代码的处境是为了写代码,为了易于管理和阅读,你的代码通常会分布在多个文件中,在发布的时候就不需要那样了,而且浏览器还不一定吃你的源代码 (比如你写的是 SCSS,CoffeeScript,模块化的 JS,JSX……),所以需要有编译 -> 打包这个过程,其中包括把你的源代码变成浏览器吃的代码,自动前缀,最小化,连接,混淆等操作,一般最后输出就是一个整的 CSS 或者 JS 文件。

          例如:你的 a.js 用了最新的 ES7 语法 async/await,在旧版本的浏览器会导致错误,所以打包过程基本上是

         而这些都是 运行在 NodeJS 环境下的 Webpack 去做的。
  • 这个是在处理依赖管理的时候遇到的包,指的是更接近软件包的概念,例如你写程序的时候用了一些别人的“库”或者“组件”,实际的体现就是你通过 npm,bower 等依赖管理(或者叫包管理器)来管理你的项目的外部依赖。比如你在你的项目里面用了一个 bootstrap,通常的方式是你可以直接下载官网发布的程序,然后解压,放到你的项目文件夹里面,但是有了 bower 之后,你就可以直接通过 bower 一键来帮你完成这些事情,在你的程序很复杂,用的库很多的时候,手动一个个下载和更新那就是非常繁琐的事情了,所以有了这样的依赖管理工具来帮助你处理这 些琐事,同时你自己的项目也是作为一个“包”来被处理的,通过记录你的“包”依赖的其他包的信息,然后那些依赖管理工具就能够自动帮你搞定后面的事情了。
  • 总结:在项目开发的时候,我们用到很多浏览器所不能识别的代码,例如es7中的一些写法【这意味着您可以编写您的nodeJS版本支持的所有现代的ES6-7-8-9javajscript.由于JavaScript的发展速度如此之快,但浏览器的升级速度可能会有点慢,有时在web上你会被旧的JavaScript/ECMScript版本所困扰。你可以在将代码发布到浏览器之前使用babel将代码转换为ES5兼容,但在NodeJSzhong ,你不需要它】、某些模块化,我们需要打包工具将这个转化为浏览器所能识别并执行的代码,而打包工具(程序)的运行,需要一个运行环境,这个运行环境就是nodejs。因此,一般前端开发都需要用到nodejs。即前端资源构建工具:前端资源是指浏览器不认识的 web 资源, 比如 sass、less、ts,包括 js 里的高级语法。这些资源要能够在浏览器中正常工作,必须一一经过编译处理,将原本浏览器不能识别的规范和各种各样的静态文件进行分析,压缩,合并,打包,最后生成浏览器支持的代码。而 webpack 就是可以集成这些编译工具的一个总的构建工具。       

      【三】NodeJS的安装与配置

                  Node.js安装包及源码下载地址为:https://nodejs.org/en/download/
                  安装很简单,在此就不详细写出来步骤了。安装完成后,配置环境变量。输入node --version,即可检查NodeJS版本。

                   

      【四.NodeJS包管理工具-npm】【参考网址:https://zhuanlan.zhihu.com/p/24357770)】

                接上述,webpack,jQuery等模块都在npm仓库(registy)中,在项目中,需要用到这些模块的时候,可以通过npm install命令下载这些模块,因此需要安装npm。现在nodejs和npm集成,因此安装了node同时也安装了npm.

                 npm就是包管理工具,即JavaScript模块管理工具.npm由三个不同的部分组成:

                                                                                                                                      1.网站(npm官网);

                                                                                                                                      2.注册表(保存包的数据库)即保存模块的数据库。

                                                                                                                     3.命令行界面(CLI):通过命令行或终端运行,实现用户与npm服务器交互

                 npm的使用:npm安装好后,直接在项目的目录下执行npm命令就可以了。 npm的命令很多,npm init ; npm install  模块等下面一一介绍。

        五package.json—npm init命令】   

                   在项目搭建时,我们在创建项目名myvueapp之后(接下来都以myvueapp为例)。在这个目录下,需要先执行npm init命令,创建myvueapp模块的package.json文件。操作步骤:npm init 一直回车,知道执行is this...输入yes,就创建好这个文件,这个文件自动被创建在myvueapp文件夹中即根目录下。(注意:项目名称不能用大写)

                      

                      package.json是什么?为什么一定要用这个文件呢?   

                       每个项目(包括npm上下载下来的包或者其他nodeJS项目)的根目录下,一般都有一个package.json文件,它定义了这个项目(如myvueapp)的所有信息,如:所需要的各种模块,以及项目的配置信息,比如名称,版本,许可证、如何启动项目、运行脚本等元数据。即这个文件可以在搭建项目之初动手创建,也可以通过npm init命令生成。

                  为什么一定要用packa.json呢?

                      1.我们通过npm install 命令下载模块的时候,必须有这个package.json文件,即myvueapp文件夹下必须包含package.json文件,然后才能从NPM安装软件包。当我们通过npm init之后,我们npm install 模块才能成功,并且同时会自动在package.json的属性dependencies对象中新增一条这个模块的信息.就这个文件的dependentcies中记录着myvueapp这个模块的依赖信息及这个项目依赖哪些模块即需要用到哪些第三方插件或者框架(库)等。

                     2.当我们搭建好项目之后,myvueapp项目的其他同事,拉下来代码之后,只需要执行npm install 就可以自动下载myvueapp项目所依赖的资源(如jQuery,vue,webpack等),而不需要自己再去了解需要哪些依赖然后再去按个npm install 模块了。

                      执行npm init之后,同时还会创建package-lock.json.这个一般记录着dependencies中模块对应的一些信息,如下图所示:

                      故而,其他同事在npm install之后,npm根据package.json中dependencies下载依赖并根据package-lock.json中保存的该依赖dependencies下载该依赖的依赖,故而,我们下载了某个模块之后,同时也下载了该模块的依赖。

                     3.项目的package.json是配置和描述如何与程序交互和运行的中心。npm cli用它来识别你的项目并了解如何处理项目的依赖关系(dependencies属性)。package.json文件使用npm run可以启动你的项目,运行脚本、安装依赖项、发布到NPM注册表以及许多其他有用的任务。npmCLI也是管理package.json的最佳方法,因为它有助于在项目的整个生命周期内生成和更新package.json文件。即package.json中的script属性:我们需要通过npm run命令来启动我们的项目,如npm run dev等,或者打包: npm run build,支持npm run命令来管理我们的项目,必须通过package.json的,必须是在package.json的scripts中配置的脚本.

               package.json属性:     

                                              1."name""myvueapp".    // 名称字段描述项目的名称,目录或应用程序的名称.

                                              2. "version"版本: //版本是您应用的当前版本。 版本字段的格式必须为xxx.npm还使用version字段来确保安装了正确的软件包版本。

                                              3. "description": " "  //description字段用于一行描述项目,否则您可以保持原样,它将使用一个空字符串。

                                              4."license""ISC"  //license属性用于为您的软件包指定许可证,以便人们知道如何使用它们以及您对它施加的任何限制。

                                              5."main": "index.js"  //main字段用于显示程序的主要入口点。在Node.js应用程序中,当使用require语句调用模块时,模块从main属性中命名的文件中导出的内容就是返回给Node.js应用程序的内容。例如:main中指定了我们通过require在项目中引入jquery框架的时候,我们引入的就是main中所指定的“dist/jquery.js”文件。其实还是jquery的打包文件。而不是整个jquery文件。    并且,main也意味着该项目打包的文件叫什么名字以及会存放到哪个文件下。即其打包文件存到哪里,别的项目要使用该模块时,从哪里取?                 

                                                                                                        

                                            6."dependencies"://项目所依赖的包名和其版本号.在给我们的项目通过npm install 模块A,当这个模块A下载成功之后,就自动在这个属性中添加一条记录;并且在package-lock.json中也添加一条记录,并且会在该记录的dependencies属性中列出模块A的依赖。一个项目搭建好之后,别的同事拉下来代码之后,只需要执行npm install即根据这个属性的值以及package-lock.json中这个属性的值就可以将我们的项目myvueapp所有的依赖都自动下载下来。

                                             {

                                                " react""^16 . 12 . 0 ",

                                                 "react-dom""~ 16 . 12 . 0 ",

                                                 "react-router-dom"" 5 . 1 . 2 ",

                                                  "react-scripts"" 3 . 3 . 0 "

                                            }

                                             7."DevDependencies"://DevDependencies列出了开发和测试所需的软件包。为了将包添加到devDependencies列表中,在安装时必须通过编写--                                                                                          //save-dev将其指定为dev依赖项 

                                              8."scripts":(npm run)

                                                 npm不仅可以用于模块管理,还可以用于执行(npm run)脚本即npm允许在package.json文件里面定义可执行的脚本。如下图所示:

                                           

                                                  如上图所示:scripts是一个对象,key是脚本的简称,value就是具体的可执行脚本。scripts中的脚本分两种:

                                             A.node_modules文件夹下的.bin目录下的脚本;

                                                 

                                                       如上图中,scripts的值:dev:webpack-dev-server是.bin文件夹下载的可执行文件。

                                                                                      unit、testRop这三个是bin文件下载可执行文件。双击bin下面的一个可执行文件sha:

                                                                                     

                                                                                 即bin文件夹下载可执行文件里面其实就是node  ....js。当我们npm run  某个可执行文件   就是===npm run   node .....js即在node环境中执行对应的js文件。

                                                   B. node  x.js;即npm run 这个值的key就相当于在node 环境中执行这个js文件。

                                                    如上图的e2e、build等,value值:node  ....js.当我们执行npm run  +key的时候   就是  npm  run  node   ....js即在node环境中执行对应的js文件

                                                    因此,1和2是一样的,因为bin下面的可执行文件不是普通的js文件,而是可执行文件,这个文件里面是node  ...js文件。因此,如论是1还是2,npm run命令+x 本质都是node XX.js即在node环境中执行某个js文件。 

                                                          npm run 脚本的原理 

                                                  npm run脚本的原理非常简单。每当执行npm run,就会自动新建一个shell,在这个Shell里面执行执行的脚本。因此,只要shell可以运行的脚本,就可以卸载npm脚本里面。比较特别的是,npm run新建的这个Shell,会将当前目录的node_modules/.bin子目录加入PATH变量,执行结束后,再将PATH变量恢复原样。因此,我们A中的脚本并没有加上node_modules/.bin。

                                                  scripts中的脚本还可以传递参数:  用--表示传参

                                                   如上图中的:                               

                                                    

                                                           这些参数会被保存在类似于数组的argv中,有什么作用呢?什么是argv呢?怎么用呢?请看下面章节

                                            scripts还有默认的脚本指令:就是不需要设置key就指向固定的脚本

                                                              "start ""node app.js",  //他脚本或命令(例如lint)部署更多。

                                                              "lint""eslint './src/**/*.{js

                                                           }'"                                                                                                                                      

                                                       管道式命令
                                                                  如果希望同时执行多个任务,可以借用 Linux 系统的 管道命令,将两个操作连在一起。
                                                                   |:连接两个任务
                                                                   &&:任务内部引用其他任务,子任务 先后 执行 ;亦为标签前面执行成功后,后面的才执行;
                                                                   &:任务内部引用其他任务,子任务 平行 执行;标签前后的一起执行;

 

【六 node环境及其变量】    

       如上所述,scripts中的脚本参数  如"dev":"webpack-dev-server"  --inline  --pro

       执行 npm  run dev 就是直接执行webpack-dev-server即npm run dev==webpack-dev-sever ,即webpack-dev-server是个可执行脚本,但这个脚本的内容仍旧是node  ....js即这个可执行脚本的本质是node  ....js即在node环境中执行或者运行....js文件。 故而,我们在node环境中执行脚本时添加参数就是给当前node进程添加参数,这些参数会被保存在node环境的全局对象global对象的process对象的argv属性中。那么什么是process和argv呢?   

       nodeJS环境和浏览器环境一样,都有内置的对象和方法。对于nodeJS,global就是其全局对象[相当于浏览器的window对象]也就是说在这个全局对象中,有很多全局属性和方法。如process对象、setTimeOut等。

                                                1.我们创建一个process.js文件:在这个文件中打印这个全局对象

                                                

                                                       2.现在我们在cmd中执行命令: node process.js//即在node环境中执行process脚本。结果如下:

                                                                                                                                                           

                                                  ...process的其他属性

                                                  

                                                  如上图所示,global对象一共有8个属性和方法。setTimeOut等和window中一模一样。其中,process是global对象的属性对象:process 对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制。由于它是全局的所以不需要引入,可以直接使用。process对象有很多属性,其中:argv就是process属性的一个属性.

                                            什么是nodejs进程?

                                            nodejs进程和浏览器进程一样,每通过npm  run  脚本或者直接node  ...js文件就相当于启动了一个进程,故而process中保存着本次进程的信息。现在打印process: 

                                               process:{

                                                      argv:[] //这个属性是一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数。即脚本参数即第一个元素是 process.execPath。 第二个元素是正被执行的 JavaScript 文件的路径。 其余的元素是执行脚本任的命令行参数。即执行当前命令通过--传入的参数。当前命令:node process.js   或者scripts中脚本后面  --部分。如:

                                                       

                                                       

                                                     现在分别npm run bb  npm run proces

                                                    

                                                   即process.argv中保存着当前命令行传入的参数。即只要是可执行的命令行执行时传入的参数。

                                                  process.execArgv :属性返回 Node.js 进程启动时传入的一组特定于 Node.js 的命令行选项。 这些选项不会出现在 process.argv 属性返回的数组中,也不包括 Node.js 可执行文件、脚本名称或脚本名称后面的任何选项。 这些选项可用于衍生与父进程具有相同执行环境的子进程。                                               

                                                    结果如下:

                                                    

                                                    我们再执行一次  node   process.js的时候,exexArgv的就是一个空数组。就是本次node进程启动参数

                                                    } 

                                             process.env:是 Node.js 中的一个环境对象。其中保存着系统的环境的变量信息。可使用 Node.js 命令行工具直接进行查看。                                         I. process.env 的作用?

                             1.通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为production,开发阶段设为development或staging,然后在脚本中读取process.env.NODE_ENV即可。

                                 2.要说明的是,NODE_ENV 这个名称只是开发社区的一种共识,名称内容是可以修改的。  

                                              II.为什么要配置环境变量即为什么配置NODE_ENV

                                                     情景一:在公司,一个项目一般会有开发版本、测试版本、灰度版本和线上版本,每个版本会对应相同或不同的数据库、API地址。为了方便管理,我们通常做成配置文件的形式,根据不同的环境,加载不同的文件。如果手动修改代码中加载配置文件的路径也可以,但是太麻烦。

                                                     情景二:通常情况下,我们需要针对不同环境(开发环境、集成环境、生产环境等),进行相应策略的打包(比如是否替换接口地址,代码是否压缩等)。就是通过process.env属性加以区分。   

                                                     这里的process.env就是Nodejs提供的一个API,它返回一个包含用户环境信息的对象。如果我们给Nodejs 设置一个环境变量,并把它挂载在 process.env 返回的对象上,便可以在代码中进行相应的环境判断即:采用nodejs顶层对象中的process.env(进程环境,返回一个包含用户环境信息的对象。)属性,根据各个环境的配置文件区分和切换环境。

                                                     III.process.env.NODE_ENV的定义和使用步骤

                                                     a.通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为production,开发阶段设为development或staging,然后在脚本中读取process.env.NODE_ENV即可。
                                                     b.要说明的是,NODE_ENV 这个名称只是开发社区的一种共识,名称内容是可以修改的。如果需要,你也可以把它定义为 NODE_abc或者xxx都行。下边我们按照约定就以NODE_ENV来说。

                                                    在Webpack配置文件中,经常会看到如下类似的代码:

                                                   

                                                   process.env是Nodejs提供的一个API,那么如果想用process.env.NODE_ENV,就必须先设置其值才能用。

                                                   但是如何创建并给这个process.env.NODE_ENV环境变量赋值呢?在webpack项目里,我们有两种方式:
                                                         1.可以通过设置package.json来实现。
                                                         我们通过在启动脚本命令的时候给node环境添加 NODE_ENV=developement 或者NODE_ENV=production来给process.env添加NODE_ENV属性并赋值为development或者production(这里随便赋值,只要能区分就行,只不过一般都是用这两个,也是业内约定俗成的吧)[1]。 如下图所示:
                                                         

                                                        

                                                       但它们的语法都不尽相同。这就带来两个问题:

                                                      那么问题又来了,我在Windows 开发部署的项目,可能在 Mac 系统无法正常打包,反之亦然。为了解决这个问题,有人就开发了 cross-env

                                                       cross-env是一个跨平台设置环境变量的第三方包,它可以让你只配置一行命令,就能轻松地在多个平台设置环境变量。首先先安装

                                                       

                                                      然后配置package.json就可以了

                                                                                                                                                      如上例中的,我们执行脚本命令dev,node进程就启动了,同时给该进程的全局变量process对象的env属性添加属性NODE_ENV属性,这个属性的值为development.那么,我们的整个项目在node环境中,这个属性的值一直都是development。当然,如果我们执行脚本build,node进程启动(类似于打开浏览器),但本次进程的process对象的env属性被添加的NODE_ENV属性的属性值为production,因此,我们的整个项目,在node环境中,这个属性的值一直都是producttion.

                                               2.我们也可以通过在某个配置文件或者其他文件中(只要是能在node环境中可执行的文件)设置:process.env.NODE_ENV="development"等

                                                      process对象是node环境中的一个全局的变量(相当于浏览器的window对象),因此在node环境中我们可以随便更改这个变量的值。例如:在testNode.js中给process.env.NODE_ENV赋值,即项目中的任何文件中的process.env都可以获取NODE_ENV这个变量,并且值为DEV。因为process是node环境的全局变量,因此在项目的任何一个文件中,都有NODE_ENV这个变量(注意提前,是node环境中),例如argv.js中获取环境变量,根据不同的值进行判断

                                                      

                                                      

                                                      现在我们启动node,执行node argv.js,如下图所示:

                                                      

                                                     注意两点:1.node环境:process是node环境中的变量,因此只有在node环境中才存在。如上例中,我们node argv.js,即argv.js是在node环境中执行的,因此能够获取到我们设置的NODE_ENV;

                                                                    2. 本进程:虽然process是全局对象,但是env表示本次进程的环境信息,而NODE_ENV添加到env对象中,因此我们设置的NODE_ENV和使用NODE_ENV的js文件必须是处于同一进程中,不然无效的。上面虽然我们只是node argv.js 但是我们也通过require的方式引入了testNode.js文件了:又因为process.env.NODE_ENV直接定义在文件的下面,并没有包起来,因此加载到argv.js之后就执行了,故而本次进程中,整个项目都能够获取到NODE_ENV值了。

                                                      那么,问题来了,我们还需要根据NODE_ENV这个变量来判断调用哪个接口?这是在浏览器中执行的js,而浏览器window并没有process这个对象,根本获取不到,该怎么办呢?这就用到了打包这个过程:webpack在编译的时候,使用defineplug插件将浏览器中加载的js中process.env.NODE_ENV[不一定是这个值,就看普通js中如何定义的]替换为具体的值。假如上例中,argv.js需要在浏览器中执行,那么在Defineplug插件中,会这么设置:process.env.NODE_ENV:JSON.stringify(process.env.NODE_ENV),这句话的意思是:webpack在编译的时候,对于需要打包的js,如果其里面有process.env.NODE_ENV(前面的那个)则用json中包含的那个值替代。例如:

                                                      我们在webpack.config.js中设置了NODE_ENV="dev".这里webpack.config.js肯定是在node环境中执行的,因此之前就会先执行这个文件,然后根据这个文件的配置进行编译打包。

                                                                     

                                                      在DefinePlugn插件中定义,在编译这些需要被打包的.js文件中,遇到process.env.NODE_ENV,替换为JSON中所包含的process.env.NODE_ENV这个值。这样js被编译打包完之后,这些js文件中是没有process.env.NODE_ENV(前面一个),只有其对应的具体值(后面一个)。

                                                      现在我们试试:在需要被打包的index.js和a.js中写入process.env.NODE.ENV

                                                      

                                                      

                                                      现在执行命令:npm run start

                                                       

                                                      注意:我们开启本地服务器时,会先执行webpack脚本。而webpack默认先执行配置文件webpack.config.js即先执行这个js文件,然后根据这个配置文件进行编译,最后实现打包。我们现在看一下打包的结果:

                                                       

                                                         如我们所料。

                                                        现在打开index.html,,看看控制台:

                                                         

                                                                                                                                                                                                            

【七  node_module—npm install命令即下载项目所依赖的模块

    在第五步中创建完package.json之后,就可以下载我们项目所要使用或者依赖的模块了

      前言:共享代码

      我们在开发中需要用到很多别人写的模块,比如我们想使用Jquery库,那么我们可以点击 jQuery 网站上提供的链接就可以下载 jQuery,放到自己的网站上使用,或者直接使用在线的(一般情况下都会下载下来使用);我们要使用BootStrap,就去 BootStrap 官网下载;我们要使用Underscore,就去Underscore官网下载等等......如果我们的项目中需要依赖很多的模块, 就需要去很多网站下载其对应的模块,有些程序员就受不鸟了,一个拥有三大美德的程序员 Isaac Z. Schlueter (以下简称 Isaaz)给出一个解决方案:用一个工具把这些代码集中到一起来管理吧!

       我们通过npm install命令来加载我们项目中需要的模块:1.npm install  模块   或  npm install  模块@版本号

                                                                              2.已经搭建好的项目,我们只需要npm install 就可以下载项目中所有的依赖及模块了。

               当我们执行了上面的命令之后,我们项目的根目录下面就有node_module文件夹,这个文件中就有我们下载的模块。同时,项目的package.json中有该模块的相关信息,同时,有可能node_module文件夹的.bin文件中有相关的npm 脚本,如,我们下载了webpack模块,在.bin文件夹下就有webpack  这个脚本。

               附加:./node_modules/.bin目录下的文件是脚本,这个脚本不能有.js后缀。这些脚本也是指令脚本即直接在该项目路径下的命令窗口输入这些脚本名(指令),回车,就相当于执行了该脚本。例如webpack,回车,就执行了打包,我们就能在dist目录下找到打包文件。npm run 命令会自动在环境变量 $PATH 添加 node_modules/.bin 目录,所以 scripts 字段里面调用命令时不用加上路径。所以我们在 package.json 文件内的 scripts 字段内指定任务的时候 一般无需指定脚本文件的路径,只需要将脚本放到 ./node_module/.bin/ 目录下即可,命令会在 这个目录 下自动寻找对应的脚本文件。而无需使用./node_modules/.bin/jshint**.js。    

     具体步骤

       NPM 的思路大概是这样的:

       1. 买个服务器作为代码仓库(registry),在里面放所有需要被共享的代码

       2. 发邮件通知 jQuery、Bootstrap、Underscore 作者使用 npm publish 把代码提交到 registry 上,分别取名 jquery、bootstrap 和 underscore(注意大小写)

       3. 社区里的其他人如果想使用这些代码,就把 jquery、bootstrap 和 underscore 写到 package.json 里,然后运行 npm install ,npm 就会帮他们下载代码(或者需要哪些模块,在终端命令行中执行:npm install 模块名 例如:npm install vue,回车,npm就帮我们将vue模块下载下来)

       4. 下载完的代码出现在 node_modules 目录里,可以随意使用了。

       这些可以被使用的代码被叫做「包」(package),这就是 NPM 名字的由来:Node Package(包) Manager(管理器)。 

     拓展:

    发展(npm和node.js的关系)

      Isaaz 通知 jQuery 作者 John Resig,他会答应吗?这事儿不一定啊,对不对?

      只有社区里的人都觉得 「npm 是个宝」的时候,John Resig 才会考虑使用 npm。

      那么 npm 是怎么火的呢?

      npm 的发展是跟 Node.js 的发展相辅相成的。

      Node.js 是由一个在德国工作的美国程序员 Ryan Dahl 写的。他写了 Node.js,但是 Node.js 缺少一个包管理器,而npm需要一个运行环境【这充分说明,npm的运行时在nodejs环境中执行的】,于是他和 npm 的作者一拍即合、抱团取暖,最终 Node.js 内置了 npm

      后来的事情大家都知道,Node.js 火了。随着 Node.js 的火爆,大家开始用 npm 来共享 JS 代码了,于是 jQuery 作者也将 jQuery 发布到 npm 了。

所以现在,你可以使用 npm install jquery 来下载 jQuery 代码。

      现在用 npm 来分享代码已经成了前端的标配。(这就是我们为什么下载npm,就是要下载node.js了,因为Node.js内置了npm。我们下载node.js就是为了下载npm,使用npm来下载我们所需要用到的、别人写的模块)。

      ps:require.js是对下载下来的模块引入html的管理 ,npm是下载这些模块。下面以打包工具模块为例进行讲解。 

        

     【八.】webpack打包工具[https://blog.csdn.net/m0_51060602/article/details/123553417]

     1.webpack的概念:

      webpack是一种前端资源构建工具,是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。

  • 前端资源构建工具:前端资源是指浏览器不认识的 web 资源, 比如 sass、less、ts,包括 js 里的高级语法。这些资源要能够在浏览器中正常工作,必须一一经过编译处理,将原本浏览器不能识别的规范和各种各样的静态文件进行分析,压缩,合并,打包,最后生成浏览器支持的代码。而 webpack 就是可以集成这些编译工具的一个总的构建工具。

     2.webpack的安装

      既然要用webpack,那就肯定有nodejs(因为web pack也是代码,其需要一个执行环境,那么node就是其执行环境。),有node就肯定有npm,因此,我们通过npm intall webpack 来下载资源。如下图所示:

      

      我们发现安装一个webpack,怎么有这么多的文件呢?这是因为webpack工具,还依赖其他的资源,其余的文件都是webpack所依赖的包,因此在下载webpack的时候,这些包必然会被下载下来。还可以通过一个webpack的package.json文件看其所依赖的包有哪些。

      

       3.webpack的使用

          a.打包指令

            在项目路径下的终端,直接运行webpack命令,然后回车,就实现了打包。打包的文件一般放在项目根目录的dist目录下。或者, 在package.json文件中的scripts节点下,新增dev脚本:"scripts": {

                 "dev": "webpack" 

               }

          我们可以通过npm run dev指令进行打包

         b.打包配置

             为了更合适且方便地使用配置,可以在 webpack.config.js 中对 webpack 进行配置。

            CLI(命令行接口) 中传入的任何参数会在配置文件中映射为对应的参数。

            webpack的配置可以使用两种方式:

                                                         1.使用配置文件的用法,即例如在项目根目录下创建webpack.config.js

                                                             2.不使用配置文件的用法.即在执行webpack脚本时传入配置参数,例如:webpack --enter  src/index.js -output dist/bundle.js (脚本参数会被保存到process.argv中,在webpack的某些文件中,可以通过process.argv来获取到这些参数,并且这些参数也是webpack内置的一些参数,和webpack.config.js中配置的参数一样)

                                                            注意,命令行接口(Command Line Interface)参数的优先级,高于配置文件参数。例如,如果将 --mode="production" 传入 webpack CLI,而配置文件使用的是 development,最终会使用 production

             b.1  webpack常用配置

                 I.webpack配置参数

                  webpack的所有可配置参数都在:node_modules/webpack/bin/config-yargs.js 文件中找到。

                  

                    

                   

                  

                 

                 

                

                

                

                

                

                     

                 

                 

                 以上为全部的webpack的配置参数。

                 再次强调:webpack的打包过程:使用webpack-cli处理命令行参数——>然后和配置文件进行融合(命令行参数优先级高于配置文件的option中的配置)———>根据配置融合的结果进行编译打包。

                 II.webpack常用配置参数 (以配置文件为例)                                                        

                首先在项目根目录中,创建名为webpack.config.js的配置文件。webpack.config.js是webpack的配置文件,webpack在真正开始打包构建之前,会先读取这个配置文件,从而基于给定的配置,对项目进行打包。(如果我们配置文件不叫这个名字,要修改为其他名字,则在命令行(cli)使用config[1] 进行修改。例如: webpack  --config  webpack.dev.js)在webpack.config.js配置文件中初始化以下配置:

                  module.exports = {
                                                mode[2]: 'development' //mode 用来指定构建模式,可选值有development和production和none
                                            }
                                             //mode 的可选值有三个,默认为production
                                            // 1.development 开发环境,不会对打包生成的文件进行代码压缩和性能优化,且打包速度快,// 适合在开发阶段使用。
                                           // 2.production 生产环境,会对打包生成的文件进行代码压缩和性能优化,且打包速度很慢,// 仅适合在项目发布阶段使用。

                   module.exports = {

                                                mode[2]: 'development',
                                                enter[3]:  ,//webpack打包的默认入口文件为项目根目录下的src/index.js,

                                                output[4]//默认的打包出口文件为项目根目录下的:dist/main.js文件,因此我们在项目根目录下创建配置文件之后,还需要创建dist文件。  然后在项目根目录中,创建dist文件。                                                                          当然,没有没有创建,也没关系,因为打包之后如果根目录中没有dist文件,会自动创建。
                                            }                                                       

                                            但是,开发者可以自定义打包的入口和出口文件:即enter定义打包文件的入口,output定义打包文件出口:

                                                       (https://blog.csdn.net/qq_27449993/article/details/117223338;http://www.javashuo.com/article/p-pznkvwrt-ba.html)

                                               配置文件中entry[3]接受三种形式的值:字符串数组对象。                                                                                                 

                                                                                                    1.对象entry:

                                                                                                     对象形式如下:

                                                                                                   

                                                                                                    最早介绍对象形式,是由于这个是最完整的entry配置其余形式只是它的简化形式而已。对象中的每一对属性对,都表明着一个入口                                                                                                                        文件[key:打包文件的出口文件名;value:打包文件的入口文件名]   ,所以多页面配置时,确定是要用这种形式的entry配置。

                                                                                                                     key:key的有两种:1.简单的字符串即key能够是简单的字符串,好比:'app', 'main', 'entry-1'等。而且对应着                                                                                                                                                      output.filename配置中的[name]变量。例如:

                                                                                                                                        注意:这里的path:'./output'不对,得是绝对路径例                                                                                                                                                      如:/Users/caijiaqi/Desktop/myvueapp/output

                                                                                                                               则打包后,打包到和webpack.config.j同一级的output文件夹中,最后的打包文件为:

                                                                                                                               app-enter.js即

                                                                                                                               

                                                                                                                               

                                                                                                                              2.key还能够是路径字符串。此时webpack会自动生成路径目录,并将路径的最后做为[name]

                                                                                                                               

                                                                                                                               例如:                                                                                                                  

                                                                                                                               

                                                                                                                              打包的结果为:   

                                                                                                                                                                                                                                                                                                      即key值为简单字符串和路径字符串差不多:1.出口都是path中指定的文件夹(所有出口文件的根文件夹);                                                                                                                                                                             出口文件:[name].js 

                                                                                                                                                                                                                  2. 简单字符串:key值:出口文件名                                                                                                                                                                                                                                                            即出口文件夹/出口文件

                                                                                                                                                                                                                     路径字符串:key值是指定的路(字符串最后是出口                                                                                                                                                                                                                                         文件名,是路径)即出口文件夹/ 路径                                                                                                                                                                                                                                       字符串最后一个‘/’前面的文件夹们 /路                                                                                                                                                                                                                                          径字符串最后一个'/'后面的文件名。 

                                                                                                                       value:value值有两种:1.value若是是字符串,并且必须是合理的noderequire函数参数字符串。好比文件路径:                                                                                                                                                                                 './app.js'(require('./app.js'));好比安装的npm模 块:'lodash'(

                                                                                           require('lodash'))                                        

                                                                                                                                    

                                                                                                                                    即必须是具有相对路径的的文件即可以在配置文件中找到这个文件。

                                                                                                                                                          2.value若是是数组,则数组中元素须要是上面描述的合理字符串值。数组中的文件通常是没有相                                                                                                                                                            互依赖关系的,可是又处于某些缘由须要将它们打包在一块儿。好比:

                                                                                                                                                                                                                                

                                                                                                     2.字符串entry

                                                                                                     entry: './app.js'  等价于下面的对象形式:

                                                                                                                      因此,在没有配置enter的时候,默认的打包出口文件名为main.js

                                                                                                     3. 数组entry

                                                                                                                      entry: ['./app.js', 'lodash']  等价于下面的对象形式:

                                                                                                      

                                                  配置文件中output[4]:配置 output 选项可以控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只指定一个 output 配置。output一

                                              般常用的有三个属性:path,fileName,publicPath

                                              path: 打包文件的出口根文件夹。必须是绝对路径。即path指定资源输出的位置,要求值必须为绝对路径。例如:                                                            

const path=require('path')
module.exports={
    entry:'./src/main.js',
    output:{
        filename:'bundle.js',
        //将资源输出位置设置为该项目的dist目录
        path: path.resolve(__dirname, 'dist')
    },
}

                                                               在Webpack 4之后,output.path已经默认为dist目录。除非我们需要更改它,否则不必单独配置,所以如果是webpack4以上,你可以写成:

module.exports={
    entry:'./src/main.js',
    output:{
        filename:'bundle.js',即path可以省略
    },
}

                               filename:打包文件的出口文件。即要打包的文件最后生成的打包文件。即filename的作用是控制输出资源的文件名,其形式为字符串。有两种形式:1.写成固定                                                                  例中:filename:'bundle.js' 这样,打包最后生成的打包文件是bundle.js。即所有需要打包的文件最后都被打包到bundle.js文件中。          2.[name].js                                                                即根据enter中的key进行动态生成的。

                                                 publicPath[https://blog.csdn.net/qq_20567691/article/details/84337919]:path指定打包的出口即打包文件的出口根文件,那么打包的所有资源都会被存储在这个根资源目录下。一般的文件都会被打包到path所指打包根目录对应的打包文件中,但是比如img src="./assets/bg.jpg" 那么src中所指的资源,不能被打包到打包文件中吧,webpack会将所有的url所指的所有资源都打包到path所指的打包文件出口目录中并且更换名字如(b17ca9f90ae3f7de658d31d0f720f247.jpg)【注意,webpack-dev-server打包的资源会指向项目根目录或者contentbase中所指的目录中】。webpack在解析 img src ="./assets/bg.jpg的时候,除了会将该url所指的资源打包到出口根目录下,同时还会更改引入资源的路径将img src="./assets/bg.jpg"  更改为img src=__webpack_require__.p + 更换的名字。那么__webpack_require__.p默认是空字符串;如果设置了publicPath则是publicPath的值即img src= publicPath+ 更换的名字。因此,一般情况下,我们都需要publicPath,除非我们的html和图片等资源在一个文件夹中,不然会找不到资源。以下例为例:

                                                                   对于按需加载(on-demand-load)或加载外部资源(external resources)(如图片、文件等)来说,output.publicPath 是很重要的选项。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。

                                                                   创建项目目录如下:

                                                                                        

                                                                          index.js:

                                                                                             

                                                              webpack.config.js: 

                                                                                      

                                                          style.css:

                                                                                    

                                                                                   可知在index.js中通过import MyImage from './assets/bg.jpg',以及在style.css中通过background: url("../assets/bg.jpg")                                                                                         no-repeat;同时引用了图片bg.jpg即web pack会将bg.jpg更名并存入出口根文件夹下【webpack.config.js中没有设置                                                                                                   output.publicPath】。现在运行webpack打包。打包后的目录如下:

                                                                                   

                                                                                   这时候,我们运行src下的index.htm

                                                                                  l

                                                                                   图片资源是访问不到。我们先看看浏览器解析的html:   

                                                                                   (这里有点不太一样,因为这张图                                                                                      是用了自己项目的结果)。我们从上图可知bg.jpg这个资源的路径被写为“”+b17ca9f90ae3f7de658d31d0f720f247.jpg,而相对                                                                                      于html的路径应该是dist/b17ca9f90ae3f7de658d31d0f720f247.jpg.即因为相对于这个html来说,图片资源被打包到dist文件夹                                                                                    下,而引入图片的路径url是:更换了的图片名字(_webpack_require__.p + 更换的名字,因为这时webpack_require__.p是默认

                                                                                   值“”)。故而找不到资源。

                                                                                   现在,我们给publicPath设置值:‘.dist/’就可以正确加载图片了,因为这时_webpack_require__.p 的值为:‘./dist’,因此资源路                                                                                     径为:.dist/b17ca9f90ae3f7de658d31d0f720f247.jpg .     

                                                                                                              附加http://www.javashuo.com/article/p-vvwspahi-bk.html                                      

                   module.exports = {

                                                mode[2]: 'development',
                                                enter[3]:  ,//webpack打包的默认入口文件为项目根目录下的src/index.js,

 

                                                output[4]//默认的打包出口文件为项目根目录下的:dist/main.js文件,因此我们在项目根目录下创建配置文件之后,还需要创建dist文件。  然后在项目根目录中,创建dist文件。                                                                          当然,没有没有创建,也没关系,因为打包之后如果根目录中没有dist文件,会自动创建。

                                                 module[5]:{//配置loader。module.rules 允许你在 webpack 配置中指定多个 loader。 这种方式是展示 loader 的一种简明方式,并且有助于使代码变得简洁和易于维护即                                                                                    关于 loader 的配置,我们是写在 module.rules 属性中。   

                                           rules: [//rules 是一个数组的形式,因此我们可以配置很多个 loader即所有的loader都在这个rules数组中即一个loader的配置对象就是一个数组元素loader配置对象有两个属性:一个是test,另一个是use。 
                                                    {
test:/\.css$/,// 匹配.css结尾的文件。//每一个 loader 对应一个对象的形式,对象属性 test 为匹配的规则,一般情况为正则表达式即test匹配需要打包的资源。即webpack在打包的过程中遇到test正则所匹配的资源的时候,使用对应use中指定的loader加载资源。
                                                       use: [ "style-loader", "css-loader"],//属性 use 针对匹配到文件类型,调用对应的 loader 进行处理。因为 loader 支持链式调用,链中的每个 loader 会处理之前已处理过的资源,最终变为 js 代码顺序为相反的顺序执行,即上述执行方式为 css-loader、style-loader。即use使用规则:从后往前执行loader。
                                                    },                                        例如这里,当webpack在打包的过程中,遇到引进来的、需要打包的、以.css结尾的文件,会先使用css-loader处理文件,然后使用style- loader处理。use是数组,里面的元素可以是不同类型的loader字符串;也可以是对象。
                                                    {
test:/\.(png|jpg|gif)$/,
use:[
'file-loader'
]
}
                                                   ]

                                                                  }

                                              }

                                              loadder配置在配置文件的module属性对象中。如上例所示。那么什么是loader?为什么要使用loader?

                                              在实际开发过程中,webpack默认只能打包处理以 '.js' 后缀名结尾的模块;其他非 '.js' 后缀名结尾的模块,webpack 默认处理不了。但实际工作中各种需求层出不穷,文件类型也多种                                                         多样,即在开发中往往不仅仅有基本的 js 代码处理,也需要加载 css、图片,也包括一些高级的将 ES6、TypeScript 转成 ES5 代码,将scss、less 转成 css,将 .jsx、.vue 文件转                                                       成 js 文件等。对于webpack本身的能力来说,对于这些转化是不支持的,我们需要给 webpack 扩展对应的 loader;loader 是 webpack 中一个非常核心的概念,webpack                                               可以使用 loader 来预处理文件;即需要调用 loader 加载器才可以正常打包,否则会报错 【对于vue\ts\less等是必须要打包的,因为浏览器不能识别这些文 件,必须经过打包编译                                                                        化成浏览器所能识别的html js   css等,而webpack打包工具又不能编译打包处理.js以外的文件,因此,web pack需要调用 loader 加载器才可以正常打包】。

                                                                      loader 加载器的作用: 协助 webpack 打包处理特定的文件模块,比如:

                                                                                                                                       css-loader 可以打包处理 '.css' 相关的文件
                                                                                                                                       less-loader 可以打包处理 '.less' 相关的文件
                                                                                                                                       babel-loader 可以打包处理 webpack 无法处理的高级JS语法                                        

                                            例如:                                         

                                            

                                           js代码里如果使用require导入一个样式文件style.css,webpack碰到.css后缀的文件不知所措.因为它默认只能处理以.js.json结尾的文件.这时候我们就需要借助 css对                                应的loader即css-loader和style-loader。

                        这些文件经过打包之后,当然也是打包到output配置的出口文件中即和引入这些文件的js文件打包到一个js文件中,如前面例子中的app.bundle.js文件                          中(注:尽管webpack最终将.css文件也打包到app.bundle.js文件中有些怪,但确实是这样的。对于一些资源比如图片资源则打包到出口文件的文件夹                                    中。如讲解前面的publicPath一个例子中所示。   

                               

                                          loader的使用:

                                           1.我们根据项目需要,看看需要哪些loader,不同后缀的资源或者文件需要不同的loader。

                                           2.npm install项目打包需要用到的loader。注意,安装了webpack并不会loader,而是我们需要用到什么类型的loader,就安装对应的loader。

                                                  3.常见的 loader 如下:

                  • style-loader: 将 css 添加到 DOM 的内联样式标签 style 里

                  • css-loader :允许将 css 文件通过 require 的方式引入,并返回 css 代码

                  • less-loader: 处理 less

                  • sass-loader: 处理 sass

                  • postcss-loader: 用 postcss 来处理 CSS

                  • autoprefixer-loader: 处理 CSS3 属性前缀,已被弃用,建议直接使用 postcss

                  • file-loader: 分发文件到 output 目录并返回相对路径

                  • url-loader: 和 file-loader 类似,但是当文件小于设定的 limit 时可以返回一个 Data Url

                  • html-minify-loader: 压缩 HTML

                  • babel-loader :用 babel 来转换 ES6 文件到 ES

                  • vue-loader

                                           下面具体看不同类型的文件所需的loader及其安装方法和及其使用方式                                            

                                                                   1.打包处理 css 文件https://baijiahao.baidu.com/s?id=1688659023263356290&wfr=spider&for=pc

                                                                     安装:运行以下命令,安装处理 css 文件的 loader    

                                                                                

                                                             注意,这里处理css文件同时安装了style-loader 和css-loader;然后注意webpack版本的不同,需要安装不同版本的loader加载器。 

                                  style-loader  :将模块的导出作为样式添加到 DOM 中

                                  css-loader  :解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码

                                                                    使用方法:

                                                                               在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

                                                                  

                                                                              webpack在打包.css后缀的文件的时候,会先使用css-loader 帮助webpack解析css文件,并返回css代码;然后使用style- loader在html解析加载打包出口                                                                                  js文件的时候,会在html中将style.css中的内容添加到html的style元素中。  下面我以具体的例子来进行讲解:

                                                                             例:1.我们在myvue app项目中的src中创建一个css文件夹,用于存放.css文件。

                                                                                    

                                                              2.在打包入口文件index.js文件中引入需要打包的style.css文件                                                                                

                                                              

                                                              3.我们现在来配置我们的配置文件:webpack.config.js

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
//         historyApiFallback:{
//             // rewrites: [
//             //     // { from: /^\/user/, to: '/user.html' },
//             //     { from: /^\/user/, to: '/home.html' }
//             //   ]
//         },
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    'file-loader'
                ]
            }
        ]
    },
    plugins: [
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
   ]
}

                                                             4.现在执行打包命令,先看看package.json文件中配置的脚本命令

                                                               

                                                              现在我们执行npm run build

                                                             5. 现在在我们的项目根目录中的output文件中就有打包出口文件app.bundle.js。这个文件中有index.js文件中的代码还有style.css中的样                                                                  式代码。即webpack会从打包入口文件入口,根据模块之间的依赖依次组合在一起,最后打包合并在一个打包出口文件中。 现在我们                                                                   个打包出口文app.bundle.js文件:                                                                          

                                                                                                                                           

                                                                如上图所示,index.js以及其中引入的所有模块,最后都打包到一个js文件中即打包出口js文件。

                                                                6.现在我们打开index.html,在这个html中引入打包的app.bundle.js文件。

                                                                

                                                                现在我们看看浏览器中的页面.如下图所示,当我们访问index.html的时候,浏览器服务器只返回了3个资源:index.html。 把包出口文                                                                        件夹output:打包出口js文件、图片资源

                                                                 

                                                                现在我们看看浏览器加载的html。发现了什么?我们style.css模块经过webpack打包之后,最后通过引入打包文件,将样式添加到了html的                                                                      style中。

                                                                 

                                                             注意:如果只使用css-loader,是不会将css加载到html的style中的即样式不会起到任何作用。

                                                  2.打包处理图片资源. file-loaderhttps://www.jianshu.com/p/b4807ecf0ce0;https://blog.csdn.net/qq_41893334/article/details/106191120

                                                            安装 :   npm install  file-loader

                                                                       注:这里要注意版本号,和webpack版本要匹配                                                           

                                                                    使用方法:在 webpack.config.js 的 module -> rules 数组中,添加file-loader 如下:     

                                                                           

                                                                                                                                                        

                                                          

                                                                             以第一张图为例:

                                                                                          1.先在项目根目录下的src中创建文件夹assets,在里面存储一个图片资源:bg.jpg                                                                             

                                                                              

                                                          2. 在index.js中引入该图片资源

                                                                    

                                                         3.现在配置webpack.config.js

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',       
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:['file-loader']
            }
        ]
    },
    plugins: [
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
   ]
}

                                                         4.现在看一下package.json中配置的

                                                         

                                                        5.执行npm. run build

                                                        

                                                       如上图所示,打包入口微index.js文件,web pack在打包的时候,在index.js中,遇到require .jpg格式的资源使用配置文件中的file-loader处理图片                                                                                资源。其余的js,css等资源都最后被打包到app.bundle.js文件中。而图片资源,经过处理后,需要被存储在另一个地方(毕竟其不是一行代码,而是独立的资源)如上面的配置,则

                                                                                                                                                      a:经过打包处理的图片被存储在 包根目录下,并且会更换这个图片的名字,如上图所示。

                                                                                                                                                      b:并且更改引用这个资源图片的原来的路径为:__webpack_require__.p +打包后的图 片名。                                                                                                                                                              例如img.src="assets/bg.jpg". 打包后变为img.src=__webpack_require__.p +打包                                                                                                                                                                              后的图片名而 __webpack_require__.p默认值为“”;如果output中给publicPath设置了值,则                                                                                                                                                                 __webpack_require__.p的值为publicPath设置的值。这样,引用图片就有正确的路径了。

                                                        以第二张图片配置file-loader为例,其余的配置和文件都和上面一样                                                              

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    {
                      loader:'file-loader',
                      options:{
                         name: '[name].[ext]'
                      }
                    }
                ]
            }
        ]
    },
    plugins: [
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
   ]
}

                                                       现在执行前面的打包:

                                                                                                       

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    {
                      loader:'file-loader',
                      options:{
                         name: '[name].[ext]',
outputPath:'images/' } } ]
} ] }, plugins: [ new webpack.NamedModulesPlugin(), new webpack.NamedChunksPlugin(), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }), ] }

                                                   现在打包,看一下:

                                                       

                                                       如上面的两个例子所示。我们现在来看options选项

                                                       outputPath:这个属性可以配置打包后的资源如图片等在打包根目录下的文件路径。如果不设置这个属性,打包后,图片被存储在打包文件跟路径下,                                                                                                                     设置了这个属性之后,图片被存储在打包文件根文件夹/outputPath设置的文件夹下 。如上图所示:我们现在看一下,在打包文件中看一下其构成                                                                                                       

                                                                     

                                                        name:修改打包后的图片的名称

                                                                 :"[name].[ext]" //在上面的代码中,我们新增了options选项对打包生成的文件做了一些配置,name: '[name]. [ext]'这句代码动态                                                                                                                                            获取了要打包的图片的名字和后缀(默认打包成很长的名),并将打包生成的图片的名字和后缀设置为对应的值.

                                                                 : '[name]_[hash:6]. [ext]'//这里我们给图片名添加了6位的hash值.使用hash的用处:通过在项目中为了给图片添加唯一标识,因                                                                 为有可能不同的模块会有相同名称的图片,这样在打包到同一个文件夹中时可能会发生冲突,那么我们呢                                                                 就可以给图片添加hash值来区分不同的图片   

                                                                  3.打包处理图片资源. url-loader      

                                                                  4.打包处理图片资源. babel-loader (https://zhuanlan.zhihu.com/p/35378233)                                            

                                                  webpack本身只能加载、打包js模块,并不能将一些代码转化为浏览器识别的代码。 

                                                 如今 ES6 语法在开发中已经非常普及,甚至也有许多开发人员用上了 ES7 或 ES8 语法。然而,浏览器对这些高级语法的支持性并不是非常好。因此为了让我们的新语法能 在浏                                                             览器中都能顺利运行,Babel 应运而生。Babel是一个JavaScript编译器,能够让我们放心的使用新一代JS语法。

            • babel-loader:在webpack里应用 babel 解析ES6的桥梁

            • @babel/core:babel核心模块

            • @babel/preset-env:babel预设,内置一组 babel 插件的集合

                                                 安装

                                                 在webpack中配置babel的时候,会有比较多的概念,对babel不太了解的同学经常会很茫然。简单做个总结:

                                                         当我们在webpack中使用babel的时候,首先要安装babel-core,这是babel编译库的核心包。

                                                                              

                                              之后webpack中对js文件,我们要进行编译,就需要配置,在webpack中,你需要用到babel-loader帮你来使用babel,因此,接下来我们需要安装babel- loader                                                                                                

                                                所以webpack.config.js中,你要写好下面的代码:

                                                                                                        

                                                       然后我们要理解preset这个概念,也就是在babel编译之前,babel需要知道你的编译规则,到底是以什么样的规范去编译(即我们的代码是用es几开发的)。比如说,我需要按照es6                                                         标准编译,那么你就安装一个babel-preset-es2015, 同样,如果你要按照es7来编译,那么你就安装babel-preset-es2016:

                                                       

                                              一般来说,如果你想用最新的规范做编译,直接安装babel-preset-env就可以了,它包含了 babel-preset-es2015, babel-preset-es2016, and babel-                                                                                               preset-es2017,等价于babel-preset-latest,可以编译所有最新规范中的代码:

                                                      

                                              有了编译规则之后,我们继续来配置webpack,在babel-loader中我们可以新增配置参数:

                                                     

                                             到这一步,打包的时候,babel就会自动按照最新语法规范,对我们的代码进行编译了。(即知道我们要打包ES最新版本的代码) 

                                            例如:  首先,按照上面的方式根据web pack版本安装对应版本的babel-loader. ;接着安装 babel-core. 安装 babel-preset-env,这里我们安装babel-                                                    preset-es2015  ;然后在配置文件中进行如上图中的配置:(接前面loader的配置项目配置)

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
//         historyApiFallback:{
//         },
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    {
                      loader:'file-loader',
                      options:{
                         name: '[name].[ext]',
                         outputPath: 'images/'
                      }
                    }
                ]
            },{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                      loader: 'babel-loader',
                      options: {
                        presets: ['es2015']
                      }
                    }
            }
        ]
    },
    plugins: [
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
   ]
}

                                        现在,我们在我们的入口文件index.js中写入es6语法:

                                             

                                             然后进行打包(这里配置的是npm run build)。现在我们看打包出口文件:

                                             

                                             我们发现,Babel只是把const转化为var,箭头函数转化为普通函数的写法。但是promise等都是没有转为ES5.这只是一个简单的转化。我们可以要求                                              或者配置需要将我们的代码编程转化到什么程度,就是如上的程度,那么不需要配置就是了,但是如果还去要对promise转化或者其他的转化,就需要                                              进行配置即:  

                                             那么问题来了,我们要把我们的代码编译称什么样子呢?如果是一个只要求兼容chrome浏览器的项目,chrome对很多es6语法支持的很好,所以不用做很多编译,代码就可以                                                       在 chrome下运行了。如果我的项目要兼容到很低的浏览器版本,那么肯定要把es6,7,8的语法编译成es4或者es5的语法。到底编译到什么程度,我们可以通过preset的配                                                       置项继续设置:(即我们最终希望把要打包的js打包到什么程度?如果只是在Chrome下运行,而且是很高的版本,则用上面的打包配置就够了。但是如果不是,则看后面的配                                                       置...)。下面配置需要将我们要打包的js打包到什么程度?

                                                     

                                             在presets里面,我们对babel-preset-env增加了一个配置项叫做browser,值是>1%,它的意思是,让babel做编译的时候,编译出来的语法支持所有市场占有率超过1%的浏                                                     览器。或者你可以写成这样: 

                                                    

                                            把参数变成chrome,那么打包出的代码,只能保证在chrome上运行正常,其他浏览器能不能跑就不好说了,因为你配置的打包程度就是兼容chrome即可。 

                                            这部分的配置参数很灵活,大家可以参考官方文档继续调整:Env preset · Babel

                                                   到这一步,我们就配置好了babel以es几的规则打包,并且配置了打包到什么程度。现在babel再打包则显然是达不到上面配置预置的需要打包到的程度。因此,就需要借助其                                                      他的协助了

                                            那么接下来我们继续说babel-polyfill是个什么东东。babel官网上写了很明确一句话,babel只负责对语法进行编译。当我们写尖头函数,babel会帮你把它编译成普通函数,这                                                    没有任何问题,但是,比如说我们代码里使用了promise,babel打包出来的代码其实还是promise,在低版本浏览器里,promise并不支持,但是babel并不会帮你处理,因为这                                                    不是语法编译层面需要做的事情。不转换新的API包括,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象。那么,因为我们在上面配置                                                     了要打包到什么程度,当然如果是比较高版本的chrome浏览器,这些这样打包就可以了,但是我们前面通过babel-preset-env预设了要打包到什么程度,很显然babel这么简单                                                    的转化肯定是不行的,因此我们需要其他的插件来协助babel进行打包即babel-polyfill即于是,如果我们要让打包出来的代码能兼容低版本浏览器,还要考虑到promise,Set这样                                                    的新语法低版本浏览器不兼容的问题,这时候babel-polyfill就出场了。你只需要全局安装一下babel-polyfill:
                                                  

                                           然后在项目中使用一下它,你的代码就不存在刚才提到的兼容性问题了。即然后,使用 import 将其引入到我们的主 bundle 文件

                                          

                                          如上例中,我们在index.js中写入 import 'babel-polyfill'. ,然后我们打包,在浏览器中引入打包文件,就不会因为浏览器不支持es高版本的问题报错                                                了。(注:注意,这种方式优先考虑正确性,而不考虑 bundle 体积大小。)为了安全和可靠,polyfill/shim 必须运行于所有其他代码之前,而且需要                                             同步加载,或者说,需要在所有 polyfill/shim 加载之后,再去加载所有应用程序代码。社区中存在许多误解,即现代浏览器“不需要”polyfill,或者                                                polyfill/shim 仅用于添加缺失功能 - 实际上,它们通常用于修复损坏实现(repair broken implementation),即使是在最现代的浏览器中,也会出现这                                             种情况。因此,最佳实践仍 然是,不加选择地和同步地加载所有polyfill/shim,尽管这会导致额外的 bundle 体积成本。

                                          babel-polyfill的机制原理:                                   

                                         

                                         因此我们发现直接用babel-polyfill会有一些坑,第一个坑是污染全局环境,比如说低版本浏览器没有Set,但是babel-polyfill会在全局变量里加一个Set。再一个问题是,会造                                                    成代码冗  余,举个例子,多个模块用到Promise,每个模块里都有可能独立存在一个对Promise做兼容性的代码。所以,使用babel-polyfill可以解决兼容性问题,但并不是最佳                                                    方 案,于是,出现了  对babel-polyfill的优化babel-plugin-transform-runtime使用这个插件,就可以解决上面的问题了,但这个一般用于自己开发组建或者库,如果只是                                                  项目中用,则还是使用babel- polyfill及其优化的方式】。                                

                                         babel-polyfill的优化 :即在preset 使用 useBuiltIns 选项。例如:
                                                                     或如下设置
                                                                                                                                        

                                                                   useBuiltIns:参数有 “entry”、“usage”、false 三个值                                                            

默认值是 false ,此参数决定了babel打包时如何处理 @babel/polyfill 语句。

“entry”: 会将文件中 import '@babel/polyfill' 语句 结合 targets ,转换为一系列引入语句,去掉目标浏览器已支持的 polyfill 模块,不管代码里有没有用到,只要目标浏览器不支持都会引入对应的 polyfill 模块。

“usage”: 不需要手动在代码里写 import '@babel/polyfill' ,打包时会自动根据实际代码的使用情况,结合 targets 引入代码里实际用到部分 polyfill 模块。注:当设置 'useBuiltIns: 'usage' 时,polyfills 会在需要时自动导入。请删除 '@babel/polyfill' 的直接导入

false: 对 import '@babel/polyfill' 不作任何处理,也不会自动引入 polyfill 模块。需要注意的是在 webpack 打包文件配置的 entry 中引入的 @babel/polyfill 不会根据 useBuiltIns 配置任何转换处理。

注:上面为什么是@babel-polyfill?babble的版本比较高的话,就使用@babel-polyfill.低版本使用babel-polyfill。

有时候使用useBuiltIns会出现警告:useBuiltIns 需要使用 core-js 翻译器,因此需要安装corejs。core.js:你可能听过'babel-polyfill',babel-polyfill 融合了 core-js 和 regenerator-runtime,因此'babel-polyfill' 本质就是'corejs')安装完corejs后,就可以指定其使用版本了。

 

                                                                                     

                                                                                    经过这一些列的配置,执行打包之后,优化成功!打包出的 bundle 文件体积已缩小!

                                              babel配置文件
                                                  在上面的例子中,我们babel的配置都在webpack.config.js中。其实,Babel 有两种并行的配置文件格式,可以一起使用,也可以单独使用。
                                                  方式一:如上面,都在webpack.config.js中配置
                                                  方式二:在项目根目录下创建一个babelrc文件,把关于babel的配置放进去:

                                                              

                                                             然后,webpack.config.js 可以精简为:

                                                            

                                              babel在解析JS文件的一个过程

                  
                                              input string -> @babel/parser parser -> AST -> transformer[s] -> AST -> @babel/generator -> output string
                                           
                                                            5.打包处理 less 文件
                                                            处理需要用到 less和less-loader插件
                                                            安装
                                                                 npm install less@3.9.0 less-loader@5.0.0
                                                           使用
                                                                  

                                          6.打包处理 ts 文件(https://www.cnblogs.com/Listener-wy/p/14889708.html  https://www.jianshu.com/p/98e1cd09ffd3)

                                                  TypeScript 是一种基于 JavaScript 的强类型编程语言,它使得在前端项目开发过程中更加严谨且流畅,一定程度上保证了大型前端项目程序的健壮性。但是 TypeScript 并不可以直接运                                                    行,而是需要转换成 JavaScript 代码才可以在 Node.js 或浏览器环境下执行,因此我们需要通过“编译器”将 TS 代码转换为 JS 代码.

                                                 安装的插件                                                 

1. typescript(ts编译器即将 TypeScript 转码为 JavaScript 代码的编译器) 2. ts-loader

使用方法:

1.首先在工程目录下创建tsconfig.json,用于Typescript的编译配置;

2.在webpack.config.js中进行配置ts-loader;

1.首先在工程目录下创建tsconfig.json文件:

                                       tsconfig.json文件是用于描述将 TypeScript 转为 JavaScript 代码的配置文件。实际就是将 TS 转为 JS 的编译(器)脚手架工具,如果是一个 TS                                           的前端工 程项目,那么就可以通过项目中的 tsconfig.json 文件来自定义配置 TS 编译相关规则。由此可以知,Typescript本身的配置并不在ts-                                         loader中,而是必须要放在工程目录下的tsconfig.json中。

                                       tsconfig.json的一般配置:                                         

                                            

    其中需要注意一点:
  files 配置项值是一个数组,用来指定了待编译文件,即入口文件。
   当入口文件依赖其他文件时,不需要将被依赖文件也指定到 files 中,因为编译器会自动将所有的依赖文件归纳为编译对象,即 index.ts 依赖 user.ts 时,不需要在                   files 中   指定 user.ts , user.ts 会自动纳入待编译文件。

   配置完成后,我们可以在命令行执行 tsc 命令,执行编译完成后,我们可以得到一个 index.js 文件和一个 index.js.map 文件,证明我们编译成功。即:ts文件通过Typescript根据     tsconfig.json配置文件编译中后,就可以变为index.js文件。

  2.一般我们不执行tsc命令,我们在webpack.config.js文件中配置ts-loader

   

                                         即当webpack进行打包的时候,遇到.ts文件,就使用ts-loader进行转化即 Webpack 主要是依赖 ts-loader 实现对 TypeScript 语法的编译支持换句                                           话说,ts-loader 实际调用了 TSC 来编译 TS 文件,TSC 的配置依赖于你项目中的 tsconfig.json 文件。

                                       总结:在项目中如果使用了ts文件,那么我们我们首先需要npm install  Typescript    ts-loader   这两个插件;还需要在项目根目录下创建Typescript编                                                  译器对应的tsconfig.json配置文件(自动读取,不需要引入路径);然后在webpack.config.js中配置ts-loader。配置完这些之后,我们在                                                              webpack的过程中对于引入的ts文件,就能很好的编译成js文件了,可以实现webpack对该文件的打包。那么这里有个问题,如果我们Typescript                                                   编译器将ts文件中的代码转化为浏览器不能识别的es高版本的代码怎么办呢?这个时候,这个转化来的js文件就是普通的js文件即相当于项目中普通                                                 的js文件,继而可以使用babel-loader进行转化,如前面讲解Babel时继续转化该js文件。因此,有时候我们还需将上述的配置改为如下配置:        

                                               

                                           7.打包处理 vue 文件 

                                            .vue是不能被浏览器所识别,因此,需要经过打包处理为浏览器所能识别的代码。当然如果所有的vue组件都写在一个html中,可能不用打包。而对于.vue文件,就需要打包处                                                       理为浏览器所能识别的代码。

                                                     安装   [注意匹配对应的版本]                                    

                                            npm i vue-loader
                                            npm i vue-template-compiler    

                                            安装补充说明 :                                      

                                            [1]简单来说,vue-loader 就是处理 .vue 文件的。但是vue-loader只能用来解析.vue文件,除了涉及到 webpack 中 vue-loader 的使用方法外,还包括 .vue 文件的一些独                                                        有使用方法。即单独一个 vue-loader 并不能解决问题,因此还需要一些其他的东西。

                                                      a.vue-template-compiler:关于他的作用,根据 readme.md 文件中所介绍的:

                                                         

                                                 大致意思就是说:这个用于把 Vue 的模板文件(应该指.vue)预编译为渲染函数,避免运行时再编译带来的性能开销。(就是说 .vue 文件,在 js 执行时再拆开,然后使                                                           用是会消耗很多性能的。)一般情况下,不需要单独用它,用 vue-loader 就行了(但是你却需要单独安装它,因为vue-loader中需要使用它,并且安装 vue-                                                                       loader 时 是不带这个 的)。即应该将 vue-loader 和 vue-template-compiler 一起安装——除非你是使用自行 fork 版本的 Vue 模板编译器的高阶用户:

                           vue-template-compiler 需要独立安装的原因是你可以单独指定其版本。

 

                                                

 

                                                       注:安装了vue-template-compiler之后也不需要做任何的配置,只需要安装就行,vue-loader在编译的时候使用到这个loader。

                                                 b.vue-loader的特性: 

                                                 Vue Loader 还提供了很多酷炫的特性 

              • 允许为 Vue 组件的每个部分使用其它的 webpack loader,例如在 <style> 的部分使用 Sass 和在 <template> 的部分使用 Pug;
              • 允许在一个 .vue 文件中使用自定义块,并对其运用自定义的 loader 链;
              • 使用 webpack loader 将 <style> 和 <template> 中引用的资源当作模块依赖来处理;
              • 为每个组件模拟出 scoped CSS;
              • 在开发过程中使用热重载来保持状态。

                                                默认配置下,vue-loader只具备基础功能

                                                                                                                    【1】.vue 文件拆解:

                              • 将 .vue 文件拆解成可用的三部分

【2】HMR功能:

                              • 默认支持 HMR 功能(DEMO里已预置,执行 npm run dev 可以通过使用);
                              • 如果对规则感兴趣的,请参照官网说明:热重载 vue-loader 

3】css局部作用域: 

                              • 支持组件的 css 使用局部作用域,在 style 标签添加 scoped 即可。
                              • 例如:<style scoped></style>,这样的话,该组件内的样式只会对该组件生效(原理是给对应的组件加指定属性名,然后 css 选择器里也加上属性选择器);
                              • 支持在组件使用 scoped 属性的情况下,让某些样式影响子元素。方法是使用 /deep/ 或 >>> 关键字,该关键字前的选择器,会加局部作用域;该关键字后面的选择器,不会加局部作用域。例如.app /deep/ .child 会被编译为:··.app[data-v-04c2046b] .child·· 

                                                           vue-loader将.vue文件拆成三个部分,然后通过这三个部分对应的loader进行各自处理,当然如果还有图片等,就通过file-loader进行处理,最后webpack将这些loader                                                             处理的最终结果放在如上例中的app.build.js打包结果文件中。 

                                                 c.其他loader

                                                      vue-loader 是一个加载器,能把如下格式的 vue 组件转化成javascript模块。

                                                          

                                                     每个 *.vue 文件都包括三部分 <template>, <script> 和 <style>。vue-loader 解析文件,提取每个语言块,让他们通过需要的其他 loaders ,                                                       最后组装起来,放回 commonjs 的模块,此模块的 module.exports 就是个 vue.js 组件对象:即<style > 部分使用css- loaderstyle-                                                               loader,根据你的需要,可能还需要更多,但一般情况下,只需要你单独安装好 这2 个就行了(安装vue-laoder不会附带安装这 2 个)。                                                           <script>部分使用babel进行编译解析即babel-loader 解析文件,提取每 个语言块,让他们通过需要的其他 loaders ,最后组装起来,放回                                                           commonjs 的模块,此模块的module.exports 就是个vue.js 组件对象。 即因此我们在npm install vue-loader之前要确保已经install了css-                                                         loader、style-loader 以及babel中的各种loader以及处理图片的file-loader等                                                                                            

                                           使用

                                                 

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
module.exports = {
//    context:path.join(__dirname,'../src'),
    entry:{
        // 'app-enter':path.join(__dirname,'src','index.js')
//        'path/of/enter':'./deep-app.js',
//        'app-enter':'./app.js'
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
    // output:{
    //     filename:'bundle.js',
    //     path:path.join(__dirname,'dist')
    // },
     devServer: {
         contentBase:path.join(__dirname, "output"),
//         historyApiFallback:{
//             // rewrites: [
//             //     // { from: /^\/user/, to: '/user.html' },
//             //     { from: /^\/user/, to: '/home.html' }
//             //   ]
//         },
         open:true
     },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    {
                      loader:'file-loader',
                      options:{
                         name: '[name].[ext]',
                         outputPath: 'images/'
                      }
                    }
                ]
            }
            ,{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                      loader: 'babel-loader',
                    }
            },
            { test:/\.vue$/,
             use:{loader: 'vue-loader' }
            }
        ]
    },
    plugins: [
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
   ]
}

                                             

                                             

                                             现在执行npm run build

                                                                                                                                                       vue-loader原理:https://mbd.baidu.com/ug_share/mbox/4a83aa9e65/share?product=smartapp&tk=6626112aa486f1c032549e598198c5af&share_url=https

                               %3A%2F%2Fwjrsbu.smartapps.cn%2Fzhihu%2Farticle%3Fid%3D355401219%26isShared%3D1%26_swebfr%3D1%26_swebFromHost%3Dbaiduboxapp&domain=mbd.baidu.com)

                                                    以上例执行npm run build为例,在执行打包命令之后,vue-loader怎么处理.vue文件包含多种格式的内容:style、script、template以及自定义block,vue-loader 如何.                                                            分别处理这些内容? 2. 针对不同内容块,vue-loader 如何复用其他loader?比如针对 less 定义的style块,vue-loader 是怎么调用 less-loader 加载内容的?

                                                          举个转换过程的例子:

                                                                                     //原始代码 import xx from './index.vue'; 

                                                                                        执行webpack命令之后,当执行到这一句的时候,【调用vue-loader即命中 vue-loader】。vue-loader进行处理.vue文件即:

                1. 调用 @vue/component-compiler-utils 包的parse函数,将SFC 文本解析为AST对象
                2. 遍历 AST 对象属性,转换为特殊的引用路径                                                                                       

对于上述 index.vue 内容,转换结果为

import { render, staticRenderFns } from "./index.vue?vue&type=template&id=2964abc9&scoped=true&"

import script from "./index.vue?vue&type=script&lang=js&"

export * from "./index.vue?vue&type=script&lang=js&"

import style0 from "./index.vue?vue&type=style&index=0&id=2964abc9&scoped=true&lang=css&"

                                                                              即这些路径都对应原始的 .vue 路径基础上增加了 vue 标志符及 type、lang 等参数。即将.vue文件拆分为三个部分

                                                                           【执行pitcher

                                                                            如前所述,vue-loader 插件会在预处理阶段插入带 resourceQuery 函数的 pitcher 对象:

                                                                            

                                                                           其中, resourceQuery 函数命中 xx.vue?vue 格式的路径,也就是说上面vue-loader 转换后的import 路径会被pitcher命中,做进一步处理:                                                                                                  pitcher的逻辑比较简单,做的事情也只是转换import路径即核心功能是遍历用户定义的rule数组,拼接出完整的行内引用路径,例如:上例中vue-l                                                                                         oader处理之后的import script from "./index.vue?vue&type=script&lang=js&",经过pitcher处理之后变为(即加对应loader的路径的):

                                         import mod from "-!../../node_modules/babel-loader/lib/index.js??clonedRuleSet2[0].rules[0]
                                         .use!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./index.vue?vue&type=scri
                                         pt&lang=js&";
                                                                          当然template以及style部分也如上所示即加上对应loader的路径
                                        再次执行vue-loader】即根据行内路径规则按序调用loader 

   通过上面 vue-loader -> pitcher 处理后,会得到一个新的行内路径,例如:

import mod from "-!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-2[0].rules[0].use!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./index.vue?vue&type=script&lang=js&";

  以这个import语句为例,之后webpack会按照下述逻辑运行:

                  • 调用 vue-loader 处理 index.js 文件(即再次执行vue- loader):调用 babel-loader 处理上一步返回的内容(即调用路径中的loader处理对应的部分)并且通过selectBlock方法将经过对应loader处理后的内容返回。即大概流程如下:

                                                                       【webpack将返回的三个部分最后都丢进打包结果文件中。】到这里对应.vue文件大致打包完成。

                                                                       【附加一】上述的vue- loader的使用是针对15以下的版本。对于15以上的版本,使用还是有点不同 即必须安装vue- loader的插                                                                              件VueLoaderPlugin(https://vue-loader.vuejs.org/zh/。 https://vue-loader.vuejs.org/zh/migrating.html )                      

                                                                          

                                                                           具体的一些loader配置的小差异可以查看官方文档。接下来分析一下VueLoaderPlugin插件在vue- loader中起什么作用。

                                                                         

                                                                        运行过程可以粗略总结为两个阶段:

                      1. 预处理阶段:在插件 apply 函数动态修改 webpack 配置,注入 vue-loader 专用的 rules(即使用VueLoadderPlug插件进行预处理:这个插件是必须的! 它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块。例如,如果你有一条匹配 /\.js$/ 的规则,那么它会应用到 .vue 文件里的 <script> 块。)
                      2. 内容处理阶段:normal loader 配合 pitcher loader 完成文件内容转换(使用上面15版本以下的步骤处理)

                                                                            插件预处理阶段

                                                                                                                   VueLoadderPlug插件会在apply函数中扩展webpack配置信息核心代码如下:

                                                                                                                 

                                                                                                                 

                                                                                                                    拆开来看,插件主要完成两个任务:

                                                                                                                                     

 

                                                                                                                  

                                                                                                                  这里定义了三个rule,分别对应vue、js、css文件。经过 VueLoaderPlugin 转换之后的结果大概module.exports = {  module: {    rules: [      {

        loader: "/node_modules/vue-loader/lib/loaders/pitcher.js",
        resourceQuery: () => {},
        options: {},
      },
      {
        resource: () => {},
        resourceQuery: () => {},
        use: [
          {
            loader: "/node_modules/mini-css-extract-plugin/dist/loader.js",
          },
          { loader: "css-loader" },
        ],
      },
      {
        resource: () => {},
        resourceQuery: () => {},
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: [["@babel/preset-env", { targets: "defaults" }]],
            },
            ident: "clonedRuleSet-2[0].rules[0].use",
          },
        ],
      },
      {
        test: /.vue$/i,
        use: [
          { loader: "vue-loader", options: {}, ident: "vue-loader-options" },
        ],
      },
      {
        test: /.css$/i,
        use: [
          {
            loader: "/node_modules/mini-css-extract-plugin/dist/loader.js",
          },
          { loader: "css-loader" },
        ],
      },
      {
        test: /.js$/i,
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: [["@babel/preset-env", { targets: "defaults" }]],
            },
            ident: "clonedRuleSet-2[0].rules[0].use",
          },
        ],
      },
    ],
  },
};

转换之后生成6个rule,按定义的顺序分别为:

                        1. 针对 xx.vue&vue 格式路径生效的规则,只用了 Vue-loader 的 pitcher 作为loader
                        2. 被复制的css处理规则,use 数组与开发者定义的规则相同
                        3. 被复制的js处理规则,use 数组也跟开发者定义的规则相同
                        4. 开发者原始定义的vue-loader规则,内容及配置都不变
                        5. 开发者原始定义的css规则,用到 css-loader、mini-css-extract-plugin loader
                        6. 开发者原始定义的js规则,用到 babel-loader

                                                                                                可以看到,第2、3项是从开发者提供的配置中复制过来的,内容相似,只是 cloneRule 在复制过程会给这些规则重新定义                                                                                                                    resourceQuery 函数 (即在下面的内容处理中,使用resourceQuery进行处理路径,最后生成带有loader的路径):

配置项,与我们经常用的 test 差不多,都用于判断资源路径是否适用这个rule。这里 resourceQuery 核心逻辑就是取出路径中的lang参数,伪造一个以 lang 结尾的路径,传入rule的condition中测试路径名对该rule是否生效,例如下面这种会命中 /.js$/i 规则: 

                                                  import script from "./index.vue?vue&type=script&lang=js&" 

                                                                                          Vue-loader 正是基于这个规则,为不同内容块 (css/js/template) 匹配、复用用户所提供的 rule 设置。

                                                                       附加二打包后的.vue文件被分为三个部分,那么该组件(被打包后的.vue文件)如何引入根组件中呢?【已解决】参考文献:

                                                                                                    https://blog.csdn.net/qq_14993591/article/details/121190981 ;https://blog.csdn.net/pfourfire/article/details/124682597

                                                                                                 这就需要在根组件中使用render选项。接下来我们看看render选项。
                                                                                                 【1】即我们在打包入口文件index.js中这样写:
                                                                                                 

                                                                                   ps:在es6中这样写:render: h => h(App);在vue1.x中不能用render,而是需要写为:将上述的render:...  部分替换为

                                                      components: { App }即:

                                                                                  【2】在index.html中这样写:

                                                                                                                                                                    通过上述配置,我们就能将myvue.vue这个.vue文件打包的之后的组件(被打包为三部分)挂载到根组件id="app"中。并                                                                                   且在根组件中并不需要通过组件元素引入也就是说不需要:<div id="app"><app></app></div>的方式引入

                                                                                              【3】

                                                                                  

                                                                                 现在我们打包打开index.html:

                                                                                

                                                                                原理分析:我们看到打包文件中script中js被打包的结果,为了更好理解,我们在myvue.vue的js中多加一些js代码吧:var                                                                                                  b="今天10月13号",现在看看这里的js的打包的结果:

                                                                                                

                                                                                                .vue中的<script>中的js代码被打包成如上图所示,其中,export default中的部分赋值为["a"]                                                                                                                                                                                                                      .vue中的<style>中的代码被打包成如下图所示,      

                                                                                          

                                                                                          .vue中的<template>部分被打包为:                                                                                                                                                                                               

                                                                                        index.js中的new Vue({

 el:'#app',
render:function(createElement){
return createElement(App);
}
})
被打包为:

                                                                                                                                                                                        这里的createElement(__WEBPACK_IMPORTED_MODULE_1__vue_myvue_vue__["a" /* default */]);应该就是:最后                                                             在浏览器中被解析为:          

                                                                                             

                                                            即上面的的...a就相当于{template:'...',data:function(){...}}

                                                                                            那么,什么是render,什么是createElement?接下来具体学习一下

                                                                                            createElement 是 Vue.js 里面的 函数,这个函数的作用就是生成一个 VNode节点,render 函数得到这个                                                                                                      VNode 节点之后,返回给 Vue.js 的 mount 函数,渲染成真实 DOM 节点,并挂载到根节点上。                                                                                                                   我们先看看createElement函数:

                                                                                             

                                                                                             我们可以简单的理解createElement是用来创建一个vnode的工厂函数,通过传入不同的参数,会产出不同类型的                                                                                               唯一的vnode对象,这些vnode在渲染挂载后(render)与我们的真实dom节点一一对应

                                                              createElement参数

                                                                                             createElement(arg1,arg2,arg3)函数可以传入三个参数:即string或object或function。

                                                                                            
                                                                                          
                                                                                            例:string:要挂载并渲染的标签                                                                               

                                                                                                

                                                                                               打开浏览器:

                                                                                              

 

                                                                                              例:string:要挂载并渲染的组件元素  

                                                                                               

                                                                                              例:object:子组件的配置

                                                                                              

                                                                                             当我们给createElement传入一个object对象的时候,createElement会将这个对象作为子组件的配置创建一                                                                                                     vnode,然后通过render渲染并挂在到父元素中。

                                                                                            通过这些分析,那么在上面的app.bundle.js中的__WEBPACK_IMPORTED_MODULE_1__vue_myvue_vue__["a"]

                                                                                            就是给createElement函数传递的第一个参数,这个第一个参数的数据类型是object即子组件的配置。

                                                                                             

                                                                                            .vue文件被vue-loader处理等之后变为浏览器能识别的3个模块,然后被webpack扔在打包出口文件中,如前面                                                                                                所示。因此上述的_WEBPACK_IMPORTED_MODULE_1__vue_myvue_vue__["a"]为组件配置就不难理解了,这                                                                                               不知道是经过webpack处理之后的还是vue-loader处理,.vue中的内容会变为render:createElement(h)函数中                                                                                                  参数h,即上述的3种参数                                                                                    

 

                                                                                                                              

module.exports = {

                          mode[2]: 'development',
                          enter[3]:  ,//webpack打包的默认入口文件为项目根目录下的src/index.js,

                                                    output[4]//默认的打包出口文件为项目根目录下的:dist/main.js文件,因此我们在项目根目录下创建配置文件之后,还需要创建dist文件。  然                                                                      在项目根目录中,创建dist文件。当然,没有没有创建,也没关系,因为打包之后如果根目录中没有dist文件,会自动创建。

                          module[5]:{//配置loader。module.rules 允许你在 webpack 配置中指定多个 loader。 这种方式是展示 loader 的一种简明方式,并且有助                                            于使代码变得简洁和易于维护即关于 loader 的配置,我们是写在 module.rules 属性中。

                         plugin[6]:{}//插件是 webpack 的 支柱 功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!即webpack中的插件,

                                                               就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等插件目的在于解决 loader 无法实现的其他事。插件相当于webpack的钩子.                                                                函数即在webpack的不同阶段执行对应的插件,贯穿了 webpack 整个编译周期

                                                        loader和plugin区别:

                                                                                           loader主要用于转换某些类型的模块,它是一个转换器

                                                                                           plugin是插件,他是对webpack本身的扩展,是一个扩展器

                                                                                                  在使用 loader 时是不需要引用的。而在使用插件时我们必须先通过 require 引用该插件。 webpack 中有丰富的内置插                                                                                                    件和外部插件,并且允许用户自定义插件。                                   

                                                       plugin的使用过程

                                                                                          步骤一:通过 npm 安装需要使用的plugins

                                                                                          步骤二:在webpack.config.js中配置plugins

                                                                                                    配置方式:一般情况,通过配置文件导出对象中 plugins 属性传入 new 实例对象。例如:

                                                                                                                                    

                                      常用的plugin:

                                                                      1. webpack-dev-server

                                                                         作用:前面已经详细介绍过了。

                                                                     2.html-webpack-plugin(插件的基本作用就是生成html文件。)

                                                                        作用:在打包结束后,⾃动生成⼀个 html 文件,并自动把打包生成的模块(js模块或者独立打包成文件的css文件)引⼊到该 html                                                                                                中。注释:我们也可以自己创建html文件 ,然后引入打包文件。但是如果打包的结果分为好多文件,我们就需要创建很多个html来引入这些js文                                                                                                    件。而且有时候需要hash值,引入的文件名又是动态变化的,我们总不能每次都去修改index.html文件。这时候我们我们就体会到用用                                                                                                    html-webpack-  plugin 插件更方便。

                                                                                    使用html-webpack- plugin的原因即作用:

                  • 为html文件中引入的外部资源如scriptlink动态添加每次compile后的hash,防止引用缓存的外部文件问题

                  • 可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置Nhtml-webpack-plugin可以生成N个页面入口

                                                                     安装                                                                                                                                                   

                                                                          npm install html-webpack-plugin@4.5.0 -D//@3.11.0是版本号,-D 是把这个包安装在开发节点下   

                                                                    使用
                                                                           1.首先在webpack.config.js配置文件中引入这个插件:
                                                                             var htmlWebpackPlugin=required ('html-webpack-plugin')
                                                                          2.然后在plugin选项中创建这个插件的一个实例即:
                                                                             

                                                                            不配置任何选项的html-webpack-plugin插件,他会默认将webpack中的entry配置所有入口thunk(js)和extract-text-

                                                                            webpack-plugin抽取的css样式都插入到文件指定的位置。例如,面这个配置生成的html为                                                                                                                 

                                                                                       当然可以使用具体的配置项来定制化一些特殊的需求,那么插件有哪些配置项呢?我们通过源码看一下html-webpack-plugin  的配置选项:      

                                                                                      

                                                                          1.template:首先了解一下template。如果指定了这个选项,那么会根据template的值作为html模板进行创建即:创建一个一模一样的html文件到                                                                                               打包出口文件。即template指向本地模板文件的位置,支持加载器(如handlebars、ejs、undersore、html等):比如:

                                                                           

                                                                            现在,我们在webpack.config.js的plugin中配置上:

                                                                           

                                                                                      现在执行打包命令,结果为:

                                                                                      

                                                                         template配置项在html文件使用file-loader时,其所指定的位置找不到,导致生成的html文件内容不是期望的内容。
                                                                        为template指定的模板文件没有指定任何loader的话,默认使用ejs-loader。如template: './index.html',若没有                                                                                为.html指定任何loader就使用ejs-loader

                                                                         2.title: 生成的html文档的标题。配置该项,它并不会替换指定模板文件中的title元素的内容,除非html模板文件中使用了模板                                                                             引擎语法来获取该配置项值,如下ejs模板语法形式

                                                                            

                                                                        3. filename:输出文件的文件名称,默认为index.html,不配置就是该文件名;此外,还可以为输出文件指定目录位置(例                                                                                如'html/index.html')

                                                                         例如,接上面,我们增加fileName这个配置:
                                                                         

                                                                         执行打包命令,结果为:

                                                                        

 1、filename配置的html文件目录是相对于webpackConfig.output.path路径而言的,不是相对于当前项目目录结构的即默认的根路径为打包出口文件的根文件。
2、指定生成的html文件内容中的linkscript路径是相对于生成目录下的,写路径的时候请写生成目录下的相对路径(这里应该是插件自动填充的,不需要我们手动去添加)。

 

                • inject:向template或者templateContent中注入所有静态资源,不同的配置值注入的位置不经相同。

1、true或者body:所有JavaScript资源插入到body元素的底部
2、head: 所有JavaScript资源插入到head元素中
3、false: 所有静态资源css和JavaScript都不会注入到模板文件中

                • favicon: 添加特定favicon路径到输出的html文档中,这个同title配置项,需要在模板中动态获取其路径值

                • hash:true|false,是否为所有注入的静态资源添加webpack每次编译产生的唯一hash值,添加hash形式如下所示:
                  html <script type="text/javascript" src="common.js?a3e1396b501cdd9041be"></script>

                • chunks:允许插入到模板中的一些chunk,不配置此项默认会将entry中所有的thunk注入到模板中。在配置多个页面时,每个页面注入的thunk应该是不相同的,需要通过该配置为不同页面注入不同的thunk;

                • excludeChunks: 这个与chunks配置项正好相反,用来配置不允许注入的thunk。

                • chunksSortMode: none | auto| function,默认auto; 允许指定的thunk在插入到html文档前进行排序。
                  >function值可以指定具体排序规则;auto基于thunk的id进行排序; none就是不排序

                • xhtml: true|fasle, 默认false;是否渲染link为自闭合的标签,true则为自闭合标签

                • cache: true|fasle, 默认true; 如果为true表示在对应的thunk文件修改后就会emit文件

                • showErrors: true|false,默认true;是否将错误信息输出到html页面中。这个很有用,在生成html文件的过程中有错误信息,输出到页面就能看到错误相关信息便于调试。

                • minify: {....}|false;传递 html-minifier 选项给 minify 输出,false就是不使用html压缩,minify具体配置参数请点击html-minifier

                                                                     配置多个html页面

 

                                   html-webpack-plugin的一个实例生成一个html文件,如果单页应用中需要多个页面入口,或者多页应用时配置多个html时,那么                                                                   就需要实例化该插件多次;

 

                                                               即有几个页面就需要在webpack的plugins数组中配置几个该插件实例:        

                                                                 

                                     3.mini-css-extract-plugin(css部分独立打包到一个文件中。注webpack3中需要用extract-text-webpack-plugin,在webpack4之后的版本才有mini-css-extract-plugin插件)
即:对于引入的css部分一般默认都统一和js等一起打包到出口文件,在html加载的时候,将其添加到页面的<style>中。但是我们有时候需要将css部分单独领出来成一个独立的文件,然后再通过<link>的方式引入到html文件中。
例如,现在我们myapp项目中共有两处添加了css样式,一个是独立的css文件,用于js中创建的dom元素添加该样式,另一个是.vue文件中添加的样式

                                                                

                                                                现在执行打包之后,我们看到这两个css分别被添加到index.html的style标签中:

                                                             

                                                            现在我们将这些打包的css从app.bundle.js文件中提取出来:

                                                                安装npm install  extract-text-webpack-plugin.   //因为myapp中webpage是3.6.0版本,因此只能用这个插件,在webpack4.0之                                                                          后可以使用mini-css-extract-plugin

                                      使用:在配置文件中引入:

                                                                     

                                                                      现在我们在myapp项目中进行配置:

var webpack = require('webpack')
var path =require('path');
process.env.NODE_ENV='dev';
console.log(process);
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
    entry:{
       app:path.join(__dirname,'src','index.js'),
    },
    output:{
        path:'/Users/caijiaqi/Desktop/myvueapp/output',
        filename:'[name].bundle.js',
        publicPath: 'output/'
    },
     devServer: {
         contentBase:path.join(__dirname, "output"),
         open:true
     },
     externals:{
             // 加载第三方资源
             vue:'Vue'
      },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:ExtractTextPlugin.extract({
                    fallback: "style-loader", // 编译后用什么loader来提取css文件
                    use: "css-loader"
                })
            },{
                test:/\.(png|jpg|gif)$/,
                use:[
                    {
                      loader:'file-loader',
                      options:{
                         name: '[name].[ext]',
                         outputPath: 'images/'
                      }
                    }
                ]
            }
            ,{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                      loader: 'babel-loader',
                    }
            },
            { test:/\.vue$/,
             use:{loader: 'vue-loader' }
            }
        ]
    },
    plugins: [
           new HtmlWebpackPlugin({
//              filename: "html/app.html",
              template: "./index.html"
           }),
           new ExtractTextPlugin("styles.css"),
           new webpack.NamedModulesPlugin(),
           new webpack.NamedChunksPlugin(),
           new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) })
   ]
}

                                                   还是前面两个css样式,进行打包,我们看到打包的结果在output中的style.css文件中,并且,没有.vue中的样式:

                                                    

                                                    现在我们再看output中的index.html文件:即在打包的时候,先将css生成独立的css文件,然后在通过htmlwebpackplugin插件创建index.html                                                        的时候会自动将生成的独立的css文件引入到html页面中,如下图所示:

                                                   

                                                  现在我们打开这个index.html

                                                 

 

                                               css打包插件的其他配置及其用法,在使用的时候再具体看看吧。

                                              ps:截止这里,我们的webpack打包的基本配置算是完了,只能打包一些基本的,具体项目中还需要用到其他loader或者plugin,还需要看看。遗留                                                    点:上面的配置好多都是基于本地的,那么在生产或者测试环境中,如何实现的?最简单的一个例子:在生产环境中,谁执行npm run build呢?                                                   还是我们在部署环境的时候,已经用的就是打包之后的文件了。那么output中的也会被提交吧?                                                   

【八点一】

     webpack-cli:用来处理webpack命令行参数。我们在执行webpack脚本的时候,会给脚本传入一些参数,这些参数会保存在node环境的全局变量process中的argv中,在webpack-cli中会对这些参数做处理,和webpack.config.js的配置文件的webpack参数做融合(例如:webpack  -enter:"index.js"   在配置文件中是:enter:"main.js"  ,那么因为命令行高于配置文件,因此最后打包的时候,会以index.js做为入口),然后进行执行打包。一般我们不需要关注webpack-cli,只有webpack@4+版本才需要单独下载webpack-cli,webpack@5+也不需要单独下载,是webpack中内置了吧。

      从上述可知,执行webpack脚本的时候,先会使用webpack-cli对脚本参数做处理,和webpack.config.js配置文件进行merge,然后根据二者融合之后的配置文件进行打包。

     我们知道,webpack命令会执行webpack-cli包的bin。那就看下webpack-cli包。(https://blog.csdn.net/liixnhai/article/details/107352968)

  

【九】webpack-dev-server(npm run webpack-dev-server)】

     1.什么是webpack-dev-server

     webpack-dev-server是用来搭建本地服务器,即本地服务器。从字面意思也可以理解:dev-server,dev(本地),server(服务)。webpack-dev-server:webpack提供了一个可选的本地开发服务器,这个本地服务器是基于node.js搭建,内部使用express框架,可以实现想要让浏览器自动刷新显示我们修改后的结果。即:

     1.我们的本地项目代码默认是部署到这个本地服务器即当输入一个url之后,浏览器可以从这个本地服务器中请求到资源并渲染出页面。因此我们可以通过localhost来访问我们的项目,即我们一般的本地项目的页面地址为:localhost:8081//....

     2.执行webpack-dev-server这个脚本可以实现热部署即我们在修改完代码之后,不需要重新打包,webpack-dev-server会实时watch,发现文件有改动就会自动打包即执行webpack脚本。

     3.和2相同,我们经过webpack.config.js中的配置或者执行脚本传入对应的参数,就可以实现启动webpack-dev-server之后可以自动打开页面,而不需要我们自己在浏览器中输入localhost:.....url

    4.等等

    2.为什么使用webpack-dev-server

     我们自己开发的项目,在本地测试,直接可以打开页面就可以了,为什么还要使用本地服务器呢?(https://blog.csdn.net/yingzi10101118/article/details/83622729?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%2283622729%22%2C%22source%22%3A%22unlogin%22%7D

     我们直接在浏览器中直接打开我们开发好的项目,如在webstorm中写好一个index.html,直接点击浏览器图标,虽然能渲染出页面,但是我们注意到浏览器中的url:file:///E:/zjy/exercise/6/4-jsonp-jquery.html

    而我们安装了本地服务器之后,我们的页面url为:

     localhost/exercise/6/4-jsonp-jquery.html

    获取后台数据:   如果是请求AJAX网络请求数据,也可以使用file:///E:/zjy/exercise/6/4-jsonp-jquery.html或者localhost/exercise/6/4-jsonp-jquery.html。即只要后台本地的服务器是开启的,则我们就能够通过浏览器请求到后台数据(获取后台数据跟前端通过url请求到资源一样的,即我们通过后台的接口url,到指定的后台服务器中请求资源即数据。)

    既然如此,为什么还要去安装使用本地服务器呢?看下面的介绍:

      最直接的区别,很容易注意到,一个是file协议,另一个是http协议。

     --file协议更多的是将该请求视为一个本地资源访问请求,和你使用资源管理器打开是一样的,是纯粹的请求本地文件。

     --http请求方式则是通过假架设一个web服务器,解析http协议的请求然后向浏览器返回资源信息。我们所开发的html文件最后必定是会以网页的形式部署在服务器上,通过http协议访问,所以我们开发中也尽可能模拟线上环境,架设本地服务器,来避免file协议与http协议实现(使用本地服务器的原因)

      前端安装并启动了本地服务器之后,我们的本台计算机就是服务器了,然后我们可以通过localhosturl来请求到本地服务器资源,从而加载并渲染。localhost就代表本台计算机的本地服务器(本机)。即:webpack-dev-server创建一个服务器(http://localhost:8080),服务器监听指定目录下的文件,并自动打包文件,默认将打包输出文件bundle.js存于服务器的根目录中(整个项目存在与http://localhost:8080中),并具有实时更新加载页面的功能;一般安装好本地服务器之后,还需要进行配置一下,如何配置,我们看第3步。

   3.如何使用webpack-dev-server(https://www.cnblogs.com/raind/p/8637865.html)

     我们以实际例子为例来进行讲解

     我们建一个这样目录结构的项目:

     

    

     

      当我们在终端运行“webpack”命令后,目录变为:

      

     一张图复习一下webpack的机制:

    
    基于上面的步骤,我们可以打开index.html,并且实现了打包。(我们注意到url是file//:)
     

    也可以和后端进行联调等,是完整的。但是我们尽可能模拟真实的环境,因此,我们需要通过本地服务来获取我们的页面,即通过浏览器请求加载资源(虽然localhost请求浏览器解析处置后,不走三次握手的流程,但是也是模拟了从服务器请求资源即页面js等。因此我们安装本地服务器,我们项目的资源自然就存储在本地服务器中,因此我们可以通过localhost来请求服务器资源)。webpack一般配置的是webpack-dev-server本地服务器。

    安装  webpack-dev-server

    在终端中进入项目目录下,敲下npm install webpack-dev-server --save-dev回车
    
   在终端运行一段命令:
   node_modules/.bin/webpack-dev-server(这时,我们并没有在package.json文件中配置相关脚本命令)
   

   即我们运行启动本地服务器之后,我们的项目就相当于部署(running)到了本地服务器中(http://localhost:8080/),现在我们在浏览器中访问这个url,即访问服务器

   

   发现了什么?我们的服务器中就是存放着我们的项目即我们的项目部署到了本地服务器中。 

   现在我们要打开index.html,我们可以直接输入localhost:8080/dist/index.html(删除上面打包的buildle.js文件,执行webpack-dev-server会重新打包)              

     为什么我们在index.html中引入我们的打包文件会找不到呢?我们的index.html中是通过<script src="bundle.js"></script>引入,为什么找不到呢?index.html和打包文件的出口即bundle.js不是都是dist文件夹下面吗?根本原因是因为webpack-dev-server打包出来的bundle.js被"放在了"我们的服务器根目录里,在dist/html里<script src="bundle.js"></script>此时显然不能根据路径找到bundle.js。【当webpack-dev-server命令终止后,通过webpack-dev-server打包出来的文件bundle.js文件就不存在。】

     怎么解决呢?

    1.既然webpack-dev-server打包出来的bundle.js放在服务器的根目录下,那么, 我们修改index.html的引入打包文件的路径即:src="/bundle.js"(可以加载打包文件)

    2.通过在webpack.config.js中进行配置,一般都是采用第二种方式。即在webpack.config.js配置文件上加上如下配置,即配置项指定了服务器资源的根目录。比如我们打包后的文件放入 dist目录下。

     

    即:

    现在我们启动服务即webpack-dev-server,打开localhost:8080/dist/index.html就可以正常加载bundle.js文件了。

    根本原因是因为:contentBase修改服务器的默认指向,即指向了资源的根目录而不再指向项目的根目录。如上例中的配置,我们现在启动服务器,则http://localhost:8080为:

     

     即本地服务器的根目录指向了dist文件夹,而不是我们的项目根目录了。假定dist文件夹下只有bundle.js文件,index.html不在该目录下,则我们再访问 http://localhost:8080/index.html 是访问不到的。但是访问 http://localhost:8080/bundle.js 该js文件是可以访问的到的。

    即我们webpack.config.js中配置的outPath:是实际打包的文件的出口路径;对于webpack-dev-server打包的文件的出口是服务器所指向的根目录,并且不是物理存储的文件。

    但是要注意,我们此时查看本地项目中dist文件夹(如d盘下保存的我们的项目),还是只有index.html一个文件,并没有我们刚刚打包的bundle.js文件。这就充分证明,通过webpack-dev-server生成的文件(打包文件) 只存在于项目对应的在本地服务器中的项目文件结构的某个目录(路径)下,当webpack-dev-server关掉之后,这个打包文件也会随之销毁。即:webpack打包和webpack-dev-server开启服务的区别——webpack输出真实的文件,而webpack-dev-server输出的文件只存在于内存中,不输出真实的文件!(注意下面两张图的区别)

    webpack:当我们在终端运行"webpack"后:
    

    webpack-dev-server:当我们在终端运行"node_modules/.bin/webpack-dev-server后:本质是因为webpack-dev-server打包到本地服务器中的根目录中,并且,我们在本地服务器根目录中看不到该打包文件即本地服务器中只能看到具有物理空间的文件。上图中本地服务的根目录中为什么有bundle.js文件么?这不是因为执行webpack-dev-server后打包生成的bundle.js而是在启动本地之前,执行过webpack命令所生成的具有物理空间的文件。而执行webpack-dev-server所生成的打包文件我们是看不见的,在内存中,路径是我们的本地服务器所指的根目录。

    

   4.webpack-dev-server在webpack.config.js中的配置

module.exports = {
/*这里省略entry和output,参照前面写的内容*/
devServer: {
contentBase: path.join(__dirname, "dist"),//contentBase是我们今天要讲的第一个webpack-dev-server的配置属性,那么,contentBase做了什么事情呢?——它指定了服务器资源的根目录,如果不写入contentBase的值,那么contentBase默认是项目的目录。如果设置了contenBase则表示本地服务器的根目录为contentBase的值。
port:7000,//port配置属性指定了开启服务的端口号。如设置端口号为7000,运行:node_modules/.bin/webpack-dev-server,这个时候就不是默认的8080的端口了,而是我们设置的7000

host:0.0.0.0//host设置的是服务器的主机号.这里的值可以是localhost ,0.0.0.0, 127.0.0.1,自己电脑的ip等,但是这些是有区别的【有时间了可以看一下】。这里的host即服务器的主机号,也就是我们的电脑的主机号,因为启动本地服务器之后,我们的电脑就是相当于服务器了。 
historyApiFallback: {该配置项属性是用来应对返回404页面时定向跳转到特定页面的。一般是应用在 HTML5中History API 的单页应用,比如在访问路由时候,访问不到该路由的时候,会跳转到index.html页面
      // 使用正则来匹配路由
      rewrites: [
        { from: /^\/user/, to: '/user.html' },//这里的user.html和home.html都是本地服务器的根目录下的文件。我们cotentBase的值设置指向哪个文件夹,则这两个文件就应该放在哪个文件夹下。这样,当我们访问locahost:8080/user的时候(这个路径正常是访问不到资源的即应该返回404,这时候会重定向于本地服务器根目录中的user.html)
        { from: /^\/home/, to: '/home.html' }
      ]
    }
},
open:true //
启动本地服务之后,自动在浏览器中打开index.html。即当本地服务启动之后,打开本地服务器根目录(contentBase所指向的资源文件夹)中index.html。如果服务器根目录中没有,则返回404.
}

       即contenBase属性设置webpack-dev-server本地服务器的根目录;

       host:设置webpack-dev-server即本地服务器的地址;一般为localhost或者0.0.0.0或者127.0.0.1都是一样的,表示本地服务器地址;

       port:设置端口号;

       historyApiFallback:设置当访问不到资源返回4040时,重定向。(依赖contentBase)例如:

       

    

        historyApiFallback设置的路径是以本地服务器根目录为绝对路径的。上例中/home.html是值根目录下的home文件。如果我们放开contentBase注释,则我们的home.html文件需要放在dist文件下,因为此时本地服务器的根路径为dist文件夹https://www.cnblogs.com/wb336035888/p/10448873.html

       接上述配置,现在我们设置  open:true  由于这里contentBase的值将本地服务器根目录指向了myvueapp项目的dist文件夹,因此我们本地服务器的根目录为dist文件夹。现在我们启动服务器,如果dist  文件夹下有index.html,则直接打开该页面;如果没有,则   

         

    综合得知,在配置 文件中配置的devServer中的所有选项,都是围绕本地服务器配置的。 当然,webpack-dev-server中的配置也可以通过脚本参数的形式写入:

     

 

【十.NodeJS语法规则-require   module.exports|export】[https://www.bbsmax.com/A/gAJGvoMbzZ/]

          在nodeJS中可以随心所欲的编写JavaScript代码,无论是ES6还是ES7等,只要是JavaScript语言都可以执行。在nodeJS中,js文件就是一个模块(不会像浏览器环境中,除非用匿名函数包围才能成为js模块即这个文件中的方法和属性外部不能访问。)。在nodeJS中的所有文件都是一个模块。nodeJS遵循的是commonJS,因此在nodeJS环境中要执行的文件中,需要用到另一个文件,则需要通过require引入在另一个文件中通过module.exports或者export导出的内容。

           nodeJS环境javascript语法特点: 1.首先,所有的javascript需要都支持,即在nodeJS环境中可执行javascript,无论是ES几 

                                                         2.在nodeJS环境中,所有的js文件或者其他的任何文件,都是独立的模块即外部或者其他模块中不能访问模块内的任何资源。将模块中的方法或属性等提供给外部使用,必须使用exports或者module.exports导出;在一个模块中要使用另一个模块的资源,必须使用require引入。

          1.module的概念

           module是一个对象。这个对象中有一些属性是关于这个文件即模块的信息,每个模块都有一个module对象。如下图所示:

             

           1.1 exports:是一个对象,这个对象中存储着这个模块的导出内容。如上例中,通过给module.exports对象添加属性h的方式,将hello方法导出。在另一个模块中通过require("模块")将这个模块的module.exports对象引入,因此在另一个模块中,可以直接引用hello方法。本质是因为对象的引用:通过require赋值的变量指向了module.exports对象的地址,因此两个访问同一个对象,相当于var b={name:'张三'};var c=b即b和c都指向同一个地址的对象。通过例子讲解:

            例如:有一个argv.js文件和 testNode.js文件。

              

              

             require("./testNode.js")相当于将module.exports对象的地址赋值给testNode变量,因此,在argv.js中可以随便调用该对象的属性和方法。

            1.2 parent:这个属性中存放着引入该模块的模块的module对象。例如:

                 

                   

 

                   执行 node argv.js

                 执行require("./testNode.js")的时候,会依次执行testNode.js,最后将module.exports的赋值给testNode变量,因此:

                          1.先执行console.log(module)

                          2.再将testNode.js模块的module.exports对象赋值给testNode变量;

                          3. 打印testNode

                          4.执行testNode中的h方法。                    

                            

 

                      1.3 children:这个属性中存放着该模块require进来的模块的module对象

                          如上例中,我们将testNode.js中的console.log(module)放在argv.js中,则

                                                                                                                                                                                                                                                            

            2.   module.exports和exports的区别

                      一般我们都用module.exports.exports 

            3.import和require的区别

               import是ES6引入的模块。require的引入是nodeJS特有的。目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require。这也是为什么在模块导出时使用module.exports,在引入模块时使用import仍然起效,因为本质上,import会被转码为require去执行

 

        npm下载下来的资源,如何使用?以npm install jquery为例:

                   1.接前面的例子与方法,我们执行上面的命令之后,就会在对应的项目中国生成一个node_module文件。如下图所示:

                     

                        2.发现jquery是一个文件夹。【咦,这和我们通过<script src="jquery.js"></script>不一样啊,这里jquery.js就是一个js文件】为什么npm 引入的就是一个文件夹呢?

                        

                          这是因为,人家开发的时候,本来就是这样的文件,只是我通过script src引入的jquery是别人经过打包出来的js文件而已。

                  3.myplug项目的根目录下有个package.json文件,这个文件记录着这个项目通过npm下载的资源,即这个项目所依赖的包名。而这些包会放置在根目录下的、自动创建的node_modules文件夹中。

 

【十一项目中的webpack】

      一般情况下,我们在项目中很少从零开始配置webpack及前面的那么多配置,这很耗成本的,就前面那些东西零零散散看了小半年的时间而且不容易理解,所以有些框架都会有自动配置打包所需要的基本配置。比如,vue框架配置了vue-cli手脚架,即只需要安装好vue-cli手脚架之后,就会初始化配置文件以及各种文件存放的文件夹,开发人员只需要将对应的文件放在对应的位置,在执行配置好的打包命令就可以实现打包。这样的好处就是,开发人员只需要开发代码,而不需要把很多时间花费在项目搭建上。后面专门会有一个vue项目实战,具体看看如何使用vue-cli.即:   

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:

  • 通过 @vue/cli 实现的交互式的项目脚手架。
  • 通过 @vue/cli + @vue/cli-service-global 实现的零配置原型开发。
  • 一个运行时依赖 (@vue/cli-service),该依赖:
    • 可升级;
    • 基于 webpack 构建,并带有合理的默认配置;
    • 可以通过项目内的配置文件进行配置;
    • 可以通过插件进行扩展。
  • 一个丰富的官方插件集合,集成了前端生态中最好的工具。
  • 一套完全图形化的创建和管理 Vue.js 项目的用户界面。

Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。

该系统的组件#

Vue CLI 有几个独立的部分——如果你看到了我们的源代码,你会发现这个仓库里同时管理了多个单独发布的包。

CLI#

CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速搭建一个新项目,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。我们会在接下来的指南中逐章节深入介绍。

CLI 服务#

CLI 服务 (@vue/cli-service) 是一个开发环境依赖。它是一个 npm 包,局部安装在每个 @vue/cli 创建的项目中。

CLI 服务是构建于 webpack 和 webpack-dev-server 之上的。它包含了:

  • 加载其它 CLI 插件的核心服务;
  • 一个针对绝大部分应用优化过的内部的 webpack 配置;
  • 项目内部的 vue-cli-service 命令,提供 servebuild 和 inspect 命令。

如果你熟悉 create-react-app 的话,@vue/cli-service 实际上大致等价于 react-scripts,尽管功能集合不一样。

CLI 服务章节涵盖了它的具体用法。

CLI 插件#

CLI 插件是向你的 Vue 项目提供可选功能的 npm 包,例如 Babel/TypeScript 转译、ESLint 集成、单元测试和 end-to-end 测试等。Vue CLI 插件的名字以 @vue/cli-plugin- (内建插件) 或 vue-cli-plugin- (社区插件) 开头,非常容易使用。

当你在项目内部运行 vue-cli-service 命令时,它会自动解析并加载 package.json 中列出的所有 CLI 插件。

插件可以作为项目创建过程的一部分,或在后期加入到项目中。它们也可以被归成一组可复用的 preset。我们会在插件和 preset 章节进行深入讨论。

注:如前面某个地方的解释,vue-cli等手脚架搭建的框架,其内部就是按照前面的方式进行搭建的。

posted @ 2021-08-20 10:36  ccfyyn  阅读(104)  评论(0编辑  收藏  举报