vue3 - vue脚手架

104 阅读8分钟

浏览器默认是不能直接识别SFC,但使用SFC来进行开发可以极大程度的提升我们的开发概率,s所以我们进行需要自己去搭建vue开发环境

但是每创建一个vue项目都自己从零搭建一个vue开发环境,会发现搭建vue开发环境的过程很多情况下都是重复的

所以我们往往会使用自己的项目模板,我们直接在项目模板的基础上进行二次开发即可

而vue的官方为我们提供了一个工具 Vue CLI (即Vue Command-Line Interface)

该工具会通过一个可以交互的命令行界面 让我们对项目所需要的环境进行相应的配置

通过这些配置,可以快速便捷的创建出对应的vue开发环境,这类工具就被称之为脚手架

所谓脚手架 即 一个可以让我们通过配置的方式 快速生成项目模板的工具

对应的项目模板 默认内置了相关打包工具(如 webpack,vite)的配置,我们可以开箱即用,直接在模板的基础上进行二次开发

# vue的脚手架工具为 @vue/cli
# 安装
npm install @vue/cli -g

# 查看脚手架版本
vue --version

# 创建项目
vue create <项目名>

# 启动
# 1. 进入到 对应项目目录文件中
# 2,对应项目依赖默认已经安装完成,直接根据package.json中设置的启动脚本 运行项目即可
cd <项目名> && npm run serve

# 项目默认会运行在8080端口,如果端口被占用,会将端口号递增后进行尝试
# 例如 8080端口被占用,那么会自动使用8081端口

项目结构

.
├── README.md  # 项目说明文档
├── babel.config.js # babel配置文件
├── jsconfig.json # 给vscode的配置文件 以便于vscode可以给予我们更友好的开发提示 (该文件并不是必须的)
├── package.json # 项目配置文件
├── public  # 静态资源
│   ├── favicon.ico # 站点图标
│   └── index.html # 项目模板
├── src
│   ├── App.vue  # App组件
│   ├── assets
│   │   └── logo.png
│   ├── components # 子组件
│   │   └── HelloWorld.vue
│   └── main.js  # 项目入口文件
|   # 默认情况下,vue脚手架使用vue-cli-service将webpack配置进行抽离
|   # 但是我们可能需要扩展活字修改webpack配置
|   # 此时就可以将对应的配置编写在vue.config.js中
|   # 在编译的时候,vue-cli-service会将该文件中的配置 和 默认的wepack配置进行合并
└── vue.config.js

SFC结构

<template>
  <div>
    <!-- 可以将template抽离到这里进行编写 -->
    <h2>{{ title }}</h2>
  </div>
</template>

<script>
// 引入其它组件
// vue脚手架对webpack的resolve.extensions进行了配置
// 所以在vue脚手架中,引入SFC文件的时候可以省略后缀名vue
import Child from './components/Child'

// 因为SFC文件会被其它文件所引入,所以必须在这里将SFC文件进行默认导出
// 注意: 即使 该SFC没有任何的页面逻辑 也必须导出,否则其它文件无法引入和使用该SFC
export default {
  // 组件局部注册
  components: {
    Child
  },
  data() {
    return {
      title: 'Hello SFC'
    }
  }
}
</script>

<!--
  在这里编写样式
  lang 表示样式需要预先交给什么进行处理
			 --- 可以设置的值有 less scss, postcss 等
  scoped 表示该样式是局部样式,仅会在当前SFC文件中生效
-->
<style lang="scss" scoped>
</style>

jsconfig.json

jsconfig.json 是一个配置文件,主要是给vscode来进行读取的

目的是为了vscode可以更好地识别我们所正在编写的项目,并给予我们更加友好的提示信息

jsconfig.json
{
  // 编译器行为相关配置项
  "compilerOptions": {
    "target": "es5", // 代码需要被打包为es5的代码
    "module": "esnext", // 项目中是的是ESM模块
    "baseUrl": "./", // paths中的基准路径
    "moduleResolution": "node", // 模块加载规则使用node模块加载规则
    // 如果项目使用了JSX,
    // 1. preserve --- TSX -> JSX -- 后续JSX->JS将交给babel或esbuild等构建工具
    // 2. react --- TSX -> JS -- 直接将TSX转换为React.createElement(), 并可以直接在浏览器中运行
    "jsx": "preserve",
    // 路径别名 --- 简化模块的引入路径
    // 构建工具中的路径别名 --- 构建项目的时候使用 --- 在编写代码时,vscode可能并不知道存在怎么样的别名
    // jsconfig.json中的路径别名 --- 编写代码时使用 --- 方便vscode识别路径别名,并进行代码提示
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    // 告诉编辑器使用了那么库,编辑器在解析的时候,会自动引入这些库文件
    // 这些库文件本质是一些类型定义文件(d.ts) --> 导入后,可以提供更准确的提示信息
    "lib": [
      "esnext", // 可以使用最新的JS语法
      "dom", // 可以使用dom相关的语法
      // 早期的dom元素是没有可迭代对象的说法的
      // 直到ES6后,某些dom元素才可以被看成是可迭代对象
      "dom.iterable", // 可以使用迭代器相关的语法
      "scripthost" // 可以使用宿主环境特有的属性和方法
    ]
  }
}

示例

src/utils/foo/baz/math.js

export function sum(num1, num2) {
	return num1 + num2
}

vue.config.js

const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({
  transpileDependencies: true,
	// 此时脚手架在编译的时候,可以正常识别路径别名utils
  // 但是此时vscode并不知道 utils对应的是那个目录,无法提供路径提示
  configureWebpack: {
		resolve: {
			// vue-cli-service内部默认配置了一个路径别名,@
			// 对应的值为src目录
			'utils': '@/utils'
		}
	}
})

为此我们需要修改jsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "jsx": "preserve",
    "paths": {
      "@/*": [
        "src/*"
      ],

      // 配置utils别名对应文件夹,以便于vscode给予我们更好的路径提示
			"utils": [
				"src/utils"
			]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}

vue的版本

vue在解析和构建的过程中,需要将vue组件编译为vnode,再映射成真实dom

目前最常见的编写方式是使用构建工具进行构建,例如使用webpack,vite

而这些构建工具在构建的时候所使用的对应插件(如vue-loader,@vitejs/plugin-vue等

会直接对SFC进行编译操作,也就是将组件转换为含有render函数的对象,

而调用render函数会返回createElementBlock函数,调用createElementBlock会生成我们实际所需的vnode

因此使用构建工具的时候,引入的vue并不需要对应的compile,而仅仅只需要对应的runtime,也就是仅仅只包含vue在运行过程中所需要的功能,这也是vue引入的默认版本

但如果使用dom模板的方式编写vue,其并不会经过编译工具编译,所以在实际使用的时候并不能引入默认版本的vue文件,而是需要引入vue/dist/vue.esm-bundler.js

该文件中,不仅仅提供了vue的runtime代码,也提供了vue的compile部分的代码

此时我们就可以使用对应的vue来成功构建我们所需要的vue应用

样式作用域

默认情况下,SFC样式的作用域是全局的

<style>
	.title {
		color: red;
	}
</style>

<!--
	会被编译为
	.title {
		color: red;
	}
-->

我们可以给style设置scoped属性,当设置该属性后,就为SFC文件开启了组件作用域

此时SFC中的样式设置作用域仅仅只是在该文件中有效

如果当前页面内使用了插槽,因为插槽内容在哪里定义的,就是在哪里进行编译的

所以插槽内的内容虽然被渲染在了子组件中,但是其对应的样式作用域依旧是当前组件

<style lang="scss" scoped>
	.title {
		color: red;
	}
</style>

<!--
	会被编译为
	.title[data-v-7ba5bd90] {
		color: red;
	}
-->

有的时候,我们可能希望在一个组件中修改另一个组件对应的样式,

此时我们可以使用样式渗透,也就是:deep()

<style scoped>
  /*
    使用样式渗透后,该组件及其所有子组件对应的.title样式都会呈现字体红色的效果
    [data-v-7ba5bd90]属性会被单独添加到父组件的每一个元素上
  */
  :deep(.title) {
    color: red;
  }
</style>

<!--
  会被编译为
  [data-v-7ba5bd90] .title {
    color: red;
  }
  
  因此如果需要实现样式渗透就必须确保父组件或子组件中必须有一个应该是单根组件
  如果父组件和子组件都是多根组件,会因为样式无法匹配,而导致样式渗透无法生效
  所以即使Vue3支持多根组件,但是依旧推荐在绝大多数情况下,使用单根组件
-->

使用vite构建脚手架

# npm init <package>
# 就意味着 在远程 存在一个 名为 create-<package>的库
# 且create-<package> 应该是一个可以在cli中使用的工具

# 此时npm 会执行如下两个步骤
# 1. 下载 create-<package>到本地
# 2. 执行 create-<package> 命令

# 添加上对应的latest是因为这样可以保证我们每次构建脚手架的时候
# 都可以使用最新的脚手架来构建我们的项目模板
# 对于pnpm 需要使用 pnpm create vue@latest 来创建对应的脚手架
# ps: 和@vue/cli不同,create-vue创建的脚手架默认没有被git仓库所管理,需要自己手动添加git仓库
npm init vue@latest