基于 Webpack 从 0 到 1 启动一个 Vue 项目

简介: 面试总是问 Webpack 怎么样?有什么用?有几个 loader,有几个 plugin,结果一去写业务,全都是上的 Vue CLI,create-react-app,启动配置什么的,前辈们都配好了,所以这篇文章就是教学如何基于 Webpack 从 0 到 1 启动一个 Vue 项目

面试总是问 Webpack 怎么样?有什么用?有几个 loader,有几个 plugin,结果一去写业务,全都是上的 Vue CLIcreate-react-app,启动配置什么的,前辈们都配好了

所以这篇文章就是教学如何基于 Webpack01 启动一个 Vue 项目,下面是这个项目运行效果

IMG

传统启动

如果你是刚开始接触 HTML/CSS/JavaScript 三件套开始接触的前端,那么你可能比较熟悉或者比较能接受的引入 Vue 的方式可能是使用 CDN 的方式,大概如下(下面这个是我要介绍的例子)

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
  <div id="app">
    <div>
      {{ "count:" + count }}
      <button @click="handleClick">click + 1</button>
    </div>
  </div>
</body>
<script>
  new Vue({
    data() {
      return {
        count: 0,
      }
    },
    methods: {
      handleClick() {
        this.count++;
      },
    }
  }).$mount('#app');
</script>

但如果你继续深入学习的话或者看过一些学习视频,往往他们会推荐你去使用 Vue CLI 去开发 Vue 项目,因为具有一定规模的项目是需要这类脚手架的,它集成了很多功能,但是如果让你自己扔掉 Vue CLI 你能否能够在浏览器中运行呢?因为究其本质,Vue CLI 是基于 nodenpm 运作的,接下来就教你们一步步简单运行一个 Vue 项目

Webpack 启动

npm 初始化

先找个位置并在终端(或者命令行)初始化一个项目

npm init

初始化后会有一些选项,可以直接回车全部忽略,也可以根据自己意向填写

IMG

选择完成之后

IMG

这个时候系统会创建一个 package.json 文件

IMG

接下来要做的就是安装 Vue

# 文章使用的 Vue2 运行
npm install vue@2.7.10

然后你的 package.json 会有多出一个 dependencies 属性,如下

{
  "dependencies": {
    "vue": "^2.7.10"
  }
}

所以上面的例子需要改成如下写法

// <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
import Vue from "vue"

但这种写法在 HTML 文件需要使用 module 加持,而且现在的 Vue 的位置在 node_modules,不能再从 HTML 中直接导入,所以需要将上面例子中的 JS 代码脱离,命名为 main.js

import Vue from "vue";

new Vue({
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    handleClick() {
      this.count++;
    },
  }
}).$mount('#app');

html 部分命名为 index.html

<body>
  <div id="app">
    <div>
      {{ "count:" + count }}
      <button @click="handleClick">click + 1</button>
    </div>
  </div>
</body>
<script type="text/javascript" src="./main.js" charset="utf-8"></script>

PS: 为什么脚本引入放在了下面?是因为 main.js 里面包含了获取 DOM 的代码,所以为了能够获取到 <div id="app">...</div> 必须放在其下面导入

此时的文件结构

├── node_modules
├── main.js
├── index.html
├── package-lock.json
└── package.json

如果你有过 HTML 部署的经验或者你的电脑上恰好有 Live Server 这个软件,你会发现这个文件结构已经能够运行

但是效果和预期不一样,会是下面这样

IMG

为什么显示是 {{"count:" + count}} 而没有被解析呢?

这是因为在 js 中默认导出的 Vue.js 是只包含运行时而不包含编译时的,而编译时干的就是解析的活,具体可以参考这个文档,对不同构建版本的解释 - Vue.js

CDN 使用的就是 UMD 完整版的 Vue.js,而 js 中导出的是只包含运行时的 Vue.js

对于这个问题可以使用 Vue 提供的 h() 渲染函数,像下面这样

new Vue({
  // ...
  render(h) {
    return h("div", [
      "count:" + this.count,
      h(
        "button",
        {
          on: {
            click: this.handleClick,
          },
        },
        "click + 1"
      ),
    ]);
  },
  // ...
}).$mount("#app");

等同于下面的 HTML

<div>
  {{ "count:" + count }}
  <button @click="handleClick">click + 1</button>
</div>

然后在 package.jsonhtml 的导入方式中设置为 module,也可以生成同样的效果,具体详情就不多演示,因为这涉及到另一个前端工程化的大方向,也就是 Vite 类型的打包框架

当然你也使用 vue-loader,但是使用 vue-loader 的前提需要一个前提,即 Webpack

Webpack

执行

npm install webpack webpack-dev-server webpack-cli --save-dev
  1. webpack 是一个打包器,可以将不同模块的 .js 文件打包成一个 .js 文件,就比如上面的 Vue.jsmain.js 合并成一个 .js 文件
  2. webpack-dev-server 可以监视 .js 文件内容的修改,进行热更新,具体场景比如更新 DOM 的内容,然后更新到浏览器
  3. webpack-cli 可以在终端执行 webpackwebpack-dev-server

webpack 重点在于它的配置,配置如下

const webpack = require("webpack");
const path = require("path");

module.exports = {
  mode: "development",
  devServer: {
    static: {
      directory: path.join(__dirname, "public"),
    },
    compress: true,
    port: 8080,
  },
  entry: path.join(__dirname, "./main.js"),
  output: {
    publicPath: "",
    path: path.join(__dirname, "./public"),
    filename: "bundle.js",
    clean: true,
  },
};

我认为 Webpack 的配置是一定要讲的,因为有非常多的属性有默认值,关键是它还比较重要!!

我强烈建议不要随便百度你遇到的问题,请去查看 Webpack 文档,即使它有点烂

解析如下

  1. modewebpack 的运行配置
  2. entry 是打包的入口文件,这里默认为 main.js
  3. output 是打包的出口文件,也就是打包结果

    1. path 是打包文件的存放位置,由于我们目的是运行 Vue 项目,目的不在打包而是运行,所以不需要过多关注
    2. publicPath 需要重点关注,和 webpack-dev-server 紧密相连,这个属性目的在于配置使用 webpack-dev-server 启动时打包文件的引用位置
  4. devServerwebpack-dev-server 的配置选项,等同于 Live Server

    1. port 启动一个服务在 localhost:port,你可以直接通过 http://localhost:port 访问热更新的内容
    2. static

      1. directory 是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的 index.html,如上就是 http://localhost:8080

配置完 Webpack,项目的文件也需要重新调整

  1. index.html 需要迁移至 public
  2. 里面的 .js 文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>

然后还需要修改一下 package.json 的内容,修改如下

{
  "name": "vue-start-with-webpack",
  "version": "1.0.0",
  "description": "运行 vue 项目 demo",
  "main": "main.js",
  "scripts": {
    "start": "webpack server", // 启动 webpack-dev-server
  },
  "author": "poplink",
  "license": "MIT",
  "dependencies": {
    "vue": "^2.7.10"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.1"
  }
}

现在的目录结构如下

├── node_modules
├── public
|  └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js

测试运行 npm run start,结果如下

解析如下

  1. modewebpack 的运行配置
  2. entry 是打包的入口文件,这里默认为 main.js
  3. output 是打包的出口文件,也就是打包结果

    1. path 是打包文件的存放位置,由于我们目的是运行 Vue 项目,目的不在打包而是运行,所以不需要过多关注
    2. publicPath 需要重点关注,和 webpack-dev-server 紧密相连,这个属性目的在于配置使用 webpack-dev-server 启动时打包文件的引用位置
  4. devServerwebpack-dev-server 的配置选项,等同于 Live Server

    1. port 启动一个服务在 localhost:port,你可以直接通过 http://localhost:port 访问热更新的内容
    2. static

      1. directory 是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的 index.html,如上就是 http://localhost:8080

配置完 Webpack,项目的文件也需要重新调整

  1. index.html 需要迁移至 public
  2. 里面的 .js 文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>

然后还需要修改一下 package.json 的内容,修改如下

{
  "name": "vue-start-with-webpack",
  "version": "1.0.0",
  "description": "运行 vue 项目 demo",
  "main": "main.js",
  "scripts": {
    "start": "webpack server", // 启动 webpack-dev-server
  },
  "author": "poplink",
  "license": "MIT",
  "dependencies": {
    "vue": "^2.7.10"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.1"
  }
}

现在的目录结构如下

├── node_modules
├── public
|  └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js

测试运行 npm run start,结果如下

IMG

注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档

Warning
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 [`devMiddleware.publicPath`](https://webpack.docschina.org/configuration/dev-server/#devserverdevmiddleware) 选项进行修改。

这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置

对于值的取舍可以参考 Webpack 文档

vue-loader 引入

先安装 vue-loader

# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev

webpack.config.js 中添加

const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      }
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里

main.js 同目录下创建 App.vue 文件,内容如下

<template>
  <div>
    {{ "count:" + count }}
    <button @click="handleClick">click + 1</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
    },
  },
};
</script>
<style></style>

main.js 的内容修改为

import Vue from "vue";
import App from "./App.vue";

new Vue({
  render: (h) => h(App),
}).$mount("#app");

index.html 的内容修改为

<body>
  <div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>

再次运行 npm run start 结果如下

IMG

总结

.js.vue 的操作就是这么简单,但好像也没用几个 loader

注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档

Warning
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 [`devMiddleware.publicPath`](https://webpack.docschina.org/configuration/dev-server/#devserverdevmiddleware) 选项进行修改。

这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置

对于值的取舍可以参考 Webpack 文档

vue-loader 引入

先安装 vue-loader

# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev

webpack.config.js 中添加

const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      }
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里

main.js 同目录下创建 App.vue 文件,内容如下

<template>
  <div>
    {{ "count:" + count }}
    <button @click="handleClick">click + 1</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    handleClick() {
      this.count++;
    },
  },
};
</script>
<style></style>

main.js 的内容修改为

import Vue from "vue";
import App from "./App.vue";

new Vue({
  render: (h) => h(App),
}).$mount("#app");

index.html 的内容修改为

<body>
  <div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>

再次运行 npm run start 结果如下

IMG

最后检查一下热更新

IMG

最后的文件目录如下,以及 github 的例子请参考 pandoralink/vue-start-with-webpack - github

├── node_modules
├── public
|  └── index.html
├── App.vue
├── main.js
├── package-lock.json
├── package.json
└── webpack.config.js

总结

刚刚接触 Vue CLI 的同学可以对比一下 Vue CLI 创建的项目和本篇文章的文件目录对应的文件比较,对应 Vue CLI 部分原理

_PS: .js.vue 的操作就是这么简单,但好像也没用几个 `loader

相关文章
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
648 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
989 2
|
9月前
|
JavaScript 前端开发 UED
Vue 项目中如何自定义实用的进度条组件
本文介绍了如何使用Vue.js创建一个灵活多样的自定义进度条组件。该组件可接受进度段数据数组作为输入,动态渲染进度段,支持动画效果和内容展示。当进度超出总长时,超出部分将以红色填充。文章详细描述了组件的设计目标、实现步骤(包括props定义、宽度计算、模板渲染、动画处理及超出部分的显示),并提供了使用示例。通过此组件,开发者可根据项目需求灵活展示进度情况,优化用户体验。资源地址:[https://pan.quark.cn/s/35324205c62b](https://pan.quark.cn/s/35324205c62b)。
454 0
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
488 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
前端开发 JavaScript Java
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
623 13
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
560 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
670 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
资源调度 JavaScript 前端开发
Pinia 如何在 Vue 3 项目中进行安装和配置?
Pinia 如何在 Vue 3 项目中进行安装和配置?
997 4
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。