npm install vue --save
npm install @vitejs/plugin-vue @vue/compiler-sfc vite --save-dev
vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
});
package.json
{
"name": "vite2-prepare",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "^3.0.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.2.4",
"@vue/compiler-sfc": "^3.0.5",
"vite": "^2.4.0"
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src\main.js
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
src\App.vue
<template>
<img src="./assets/ico.jpg" />
<HelloWorld msg="Vue3 + Vite" />
</template>
<script setup>
//https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
import HelloWorld from './components/HelloWorld.vue'
</script>
src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
</template>
src\App.vue
<template>
+ <img src="./assets/avatar.jpg" />
</template>
<template>
<img src="./assets/avatar.jpg" />
+ <img :src="imgUrl" />
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
//https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
import HelloWorld from './components/HelloWorld.vue'
+import imgUrl from './assets/avatar.jpg'
</script>
<template>
<img src="./assets/avatar.jpg" />
<img :src="imgUrl" />
+ <div class="avatar"></div>
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
//https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
import HelloWorld from './components/HelloWorld.vue'
import imgUrl from './assets/avatar.jpg'
</script>
+<style scoped>
+.avatar{
+ width:200px;
+ height:200px;
+ background-image: url(./assets/avatar.jpg);
+ background-size: contain;
+}
+</style>
public
目录中,它应位于你的项目根目录/
根路径访问到,并且打包时会被完整复制到目标目录的根目录下public\avatar.jpg
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import {resolve} from 'path';
// https://vitejs.dev/config/
export default defineConfig({
+ resolve:{
+ alias:{
+ '@':resolve('src')
+ }
+ },
plugins: [vue()]
})
src\App.vue
<template>
+ <img src="@/assets/avatar.jpg" />
<img :src="avatarUrl" />
<div class="avatar"></div>
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
//https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
+import HelloWorld from "@/components/HelloWorld.vue";
+import avatarUrl from "@/assets/avatar.jpg";
</script>
<style scoped>
.avatar {
width: 200px;
height: 200px;
+ background-image: url(@/assets/avatar.jpg);
background-size: contain;
}
</style>
src\main.js
import { createApp } from 'vue'
import App from './App.vue'
+import './global.css'
createApp(App).mount('#app')
src\global.css
#app {
background-color: lightgrey;
}
<style>
标签有 scoped
属性时,它的 CSS 只作用于当前组件中的元素data-v-hash
的方式来使 css 有了它对应模块的标识src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
+ <a>超链接</a>
</template>
+<style scoped>
+a {
+ color: #42b983;
+}
+</style>
module
作用的style
都被保存到$style
对象中src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
+ <a :class="$style.link">超链接</a>
</template>
+<style module>
+.link {
+ color: #42b983;
+}
+</style>
<template>
<h1>{{ msg }}</h1>
+ <a :class="style.link">超链接</a>
</template>
<script setup>
+import style from './HelloWorld.module.css';
</script>
src\components\HelloWorld.module.css
.link {
color: #42b983;
}
style lang="sass"
(或其他预处理器)自动开启npm i less sass -S
src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
<a :class="style.link">超链接</a>
+ <h2>less</h2>
+ <h3>sass</h3>
</template>
<script setup>
import { reactive } from 'vue'
import style from './HelloWorld.module.css';
</script>
+<style scoped lang="less">
+@color:red;
+h2{
+ color:@color;
+}
+</style>
+<style scoped lang="scss">
+$color:green;
+h3{
+ color:$color;
+}
</style>
npm install autoprefixer --save
module.exports = {
plugins: [require("autoprefixer")],
};
>0.2%
not dead
not op_mini all
src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
<a :class="style.link">超链接</a>
<h2>less</h2>
<h3>sass</h3>
+ <div class="postcss"></div>
</template>
<script setup>
import { reactive } from 'vue'
import style from './HelloWorld.module.css';
</script>
<style scoped lang="less">
@color:red;
h2{
color:@color;
}
</style>
<style scoped lang="scss">
$color:green;
h3{
color:$color;
}
</style>
+<style scoped>
+.postcss{
+ height:50px;
+ width:200px;
+ background-color: orange;
+ transform: rotate(90deg);
+}
+</style>
cnpm install typescript @babel/core @babel/preset-env @babel/preset-typescript --save-dev
.babelrc
{
"presets": [
["@babel/preset-env"],
"@babel/preset-typescript"
]
}
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
<h2>less</h2>
<h3>sass</h3>
<div class="postcss"></div>
+ <button @click="handleClick">{{state.count}}</button>
</template>
<script setup lang="ts">
import { reactive,defineProps } from 'vue'
+defineProps({
+ msg:String
+})
+interface State {
+ count:number;
+}
+let state = reactive<State>({count:0});
+const handleClick = ()=>{
+ console.log(state.count);
+ state.count++;
+}
</script>
<style scoped lang="less">
@color:red;
h2{
color:@color;
}
</style>
<style scoped lang="scss">
$color:green;
h3{
color:$color;
}
</style>
<style scoped>
.postcss{
height:50px;
width:200px;
background-color: orange;
transform: rotate(90deg);
}
</style>
.vue
文件src\shims-vue.d.ts
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
src\vite-env.d.ts
/// <reference types="vite/client" />
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': resolve('src')
}
},
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://jsonplaceholder.typicode.com',
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, '')
+ }
+ }
+ },
plugins: [vue()]
})
src\App.vue
<template>
<img src="@/assets/avatar.jpg" />
<img :src="avatarUrl" />
<div class="avatar"></div>
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
//https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md
import HelloWorld from "@/components/HelloWorld.vue";
import avatarUrl from "@/assets/avatar.jpg";
+fetch('/api/todos/1')
+ .then(response => response.json())
+ .then(json => console.log(json))
</script>
<style scoped>
.avatar {
width: 200px;
height: 200px;
background-image: url(@/assets/avatar.jpg);
background-size: contain;
}
</style>
npm i mockjs vite-plugin-mock -D
node ./node_modules/vite-plugin-mock/node_modules/esbuild/install.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path';
+import { viteMockServe } from "vite-plugin-mock";
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': resolve('src')
}
},
server: {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
+ plugins: [vue(),viteMockServe({})]
})
mock\test.ts
import { MockMethod } from 'vite-plugin-mock';
export default [
{
url: '/api/get',
method: 'get',
response: ({ query }) => {
return {
code: 0,
data: {
name: 'vben',
},
};
},
},
] as MockMethod[];
npm install eslint eslint-plugin-vue @vue/eslint-config-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
src\components\HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
<h2>less</h2>
<h3>sass</h3>
<div class="postcss" />
<button @click="handleClick">
{{ state.count }}
</button>
</template>
<script setup lang="ts">
import { reactive,defineProps } from 'vue'
defineProps({
+ msg:{
+ type:String,
+ default:''
+ }
})
interface State {
count:number;
}
let state = reactive<State>({count:0});
const handleClick = ()=>{
console.log(state.count);
state.count++;
}
</script>
<style scoped lang="less">
@color:red;
h2{
color:@color;
}
</style>
<style scoped lang="scss">
$color:green;
h3{
color:$color;
}
</style>
<style scoped>
.postcss{
height:50px;
width:200px;
background-color: orange;
transform: rotate(90deg);
}
</style>
src\main.ts
import { createApp } from "vue";
import App from "./App.vue";
import "./global.css";
createApp(App).mount("#app");
.eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"plugin:vue/vue3-recommended",
"eslint:recommended",
"@vue/typescript/recommended",
],
parserOptions: {
ecmaVersion: 2021,
},
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
},
};
.eslintignore
*.css
*.jpg
*.jpeg
*.png
*.gif
*.d.ts
package.json
{
"name": "zhufeng-vite2-prepare",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
+ "lint":"eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --quiet",
+ "lint:fix":"eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --fix"
},
"dependencies": {
"less": "^4.1.1",
"sass": "^1.35.2",
"vue": "^3.0.5"
},
"devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^4.28.2",
+ "@typescript-eslint/parser": "^4.28.2",
"@vitejs/plugin-vue": "^1.2.4",
"@vue/compiler-sfc": "^3.0.5",
+ "@vue/eslint-config-typescript": "^7.0.0",
"autoprefixer": "^10.2.6",
+ "eslint": "^7.30.0",
+ "eslint-plugin-vue": "^7.13.0",
"mockjs": "^1.1.0",
"vite": "^2.4.0",
"vite-plugin-mock": "^2.9.1"
}
}
Prettier
npm install prettier eslint-plugin-prettier @vue/eslint-config-prettier --save-dev
{
"name": "zhufeng-vite2-prepare",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"lint": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --quiet",
"lint:fix": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --fix"
},
"dependencies": {
"less": "^4.1.1",
"sass": "^1.35.2",
"vue": "^3.0.5"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@vitejs/plugin-vue": "^1.2.4",
"@vue/compiler-sfc": "^3.0.5",
+ "@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"autoprefixer": "^10.2.6",
"eslint": "^7.30.0",
+ "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.13.0",
"mockjs": "^1.1.0",
+ "prettier": "^2.3.2",
"vite": "^2.4.0",
"vite-plugin-mock": "^2.9.1"
}
}
.eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"plugin:vue/vue3-recommended",
"eslint:recommended",
"@vue/typescript/recommended",
+ "@vue/prettier",
+ "@vue/prettier/@typescript-eslint",
],
parserOptions: {
ecmaVersion: 2021,
},
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
+ "prettier/prettier": ["error", { endOfLine: "auto" }],
},
};
cnpm i jest@next babel-jest@next @types/jest vue-jest@next ts-jest@next @vue/test-utils@next --save-dev
{
"name": "zhufeng-vite2-prepare",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"lint": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --quiet",
"lint:fix": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --fix"
},
"dependencies": {
"less": "^4.1.1",
"sass": "^1.35.2",
"vue": "^3.0.5"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@vitejs/plugin-vue": "^1.2.4",
"@vue/compiler-sfc": "^3.0.5",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"autoprefixer": "^10.2.6",
"eslint": "^7.30.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.13.0",
"mockjs": "^1.1.0",
"prettier": "^2.3.2",
"vite": "^2.4.0",
"vite-plugin-mock": "^2.9.1"
}
}
jest.config.js
module.exports = {
testEnvironment: "jsdom",
transform: {
"^.+\\.vue$": "vue-jest",
"^.+\\.jsx?$": "babel-jest",
"^.+\\.tsx?$": "ts-jest",
},
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
testMatch: ["**/tests/**/*.spec.[jt]s"],
};
tests\test.ts
import { mount } from "@vue/test-utils";
const MessageComponent = {
template: "<p>{{ msg }}</p>",
props: ["msg"],
};
test("displays message", () => {
const wrapper = mount(MessageComponent, {
props: {
msg: "Hello world",
},
});
expect(wrapper.text()).toContain("Hello world");
});
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
+ "types":["vite/client","jest"],
+ "baseUrl": "./",
+ "paths": {
+ "@": ["./src"]
+ }
},
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue","tests/**/*.spec.ts", "tests/test.ts"]
}
package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"lint": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --quiet",
"lint:fix": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --fix",
+ "test": "jest --passWithNoTests"
}
}
git commit
之前检查代码,保证所有提交到版本库中的代码都是符合规范的git push
之前执行单元测试,保证所有的提交的代码经过的单元测试git commit -m ""
中的描述信息git commit -m <type>[optional scope]: <description>
类型 | 描述 |
---|---|
build | 编译相关的修改,例如发布版本、对项目构建或者依赖的改动 |
chore | 其他修改, 比如改变构建流程、或者增加依赖库、工具等 |
ci | 持续集成修改 |
docs | 文档修改 |
feature | 新特性、新功能 |
fix | 修改 bug |
perf | 优化相关,比如提升性能、体验 |
refactor | 代码重构 |
revert | 回滚到上一个版本 |
style | 代码格式修改 |
test | 测试用例修改 |
cnpm i husky lint-staged @commitlint/cli @commitlint/config-conventional --save-dev
prepare
脚本会在npm install
(不带参数)之后自动执行husky install
命令,该命令会创建.husky/
目录并指定该目录为git hooks
所在的目录npm set-script prepare "husky install"
npm run prepare
npx husky add .husky/pre-commit "lint-staged"
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
npx husky add .husky/pre-push "npm run test"
commitlint.config.js
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2,
"always",
[
"feature",
"update",
"fixbug",
"refactor",
"optimize",
"style",
"docs",
"chore",
],
],
"type-case": [0],
"type-empty": [0],
"scope-empty": [0],
"scope-case": [0],
"subject-full-stop": [0, "never"],
"subject-case": [0, "never"],
"header-max-length": [0, "always", 72],
},
};
<script setup>
是 Vue 3 中的一个新特性,它是一个实验性的 API,用于组合式 API 的语法糖。这个特性是为了让 Vue 的单文件组件(SFC)在使用组合式 API 时更加简洁。
在 Vue 3 之前,我们通常在 export default
中定义组件的各种选项,如 data
, methods
, computed
等。但是,当我们使用组合式 API(例如 ref
, reactive
, computed
, watch
等)时,我们需要在 setup
函数中定义它们。
使用 <script setup>
,我们可以直接在 <script>
标签内部定义这些响应式属性和函数,而不需要显式地定义一个 setup
函数。
示例
不使用 <script setup>
的组件:
<template>
<div>
{{ count }}
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => {
count.value++;
};
return {
count,
increment
};
}
}
</script>
使用 <script setup>
的相同组件:
<template>
<div>
{{ count }}
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => {
count.value++;
};
</script>
如你所见,使用 <script setup>
使得代码更加简洁。
注意事项
<script setup>
时,你不能使用 data
, methods
, computed
等旧的选项 API。所有的逻辑都应该使用组合式 API。props
和 emit
。为了在 <script setup>
中使用它们,你需要从 vue
导入 defineProps
和 defineEmits
。<script setup>
是实验性的,所以在生产环境中使用前,请确保你了解其所有的细节和限制。defineConfig
是 Vite 提供的一个辅助函数,用于为 Vite 配置文件提供类型提示和类型检查。当你使用 TypeScript 来编写 Vite 的配置文件时,defineConfig
尤其有用。
为什么使用 defineConfig
?
Vite 的配置文件是一个普通的 JavaScript 或 TypeScript 文件,它默认导出一个配置对象。但是,由于这只是一个普通的对象,你可能不会得到关于该对象结构的类型提示或类型检查。
通过使用 defineConfig
,你可以确保你的配置文件正确地遵循 Vite 的配置格式,并在编写配置时获得有关可用选项的自动完成和类型检查。
如何使用 defineConfig
?
首先,确保你已经安装了 Vite:
npm install vite --save-dev
然后,在你的 Vite 配置文件中(例如 vite.config.ts
),你可以这样使用 defineConfig
:
import { defineConfig } from "vite";
export default defineConfig({
// 你的配置选项
base: "./",
plugins: [],
// ... 其他配置选项
});
这样,当你在配置对象中添加或修改属性时,你将获得 Vite 配置的相关类型提示。
注意事项
defineConfig
本身并不会改变配置的行为或执行任何逻辑。它只是一个类型辅助函数。defineConfig
并没有太大的意义,因为你不会从中获得类型提示或检查的好处。总之,defineConfig
是 Vite 提供的一个简单的辅助工具,用于增强配置文件的类型安全性和开发者体验。如果你正在使用 TypeScript 编写 Vite 配置,那么使用 defineConfig
是一个好的实践。
@vitejs/plugin-vue
是 Vite 的一个官方插件,用于支持 Vue 3 的单文件组件(SFC)。Vite 是一个由 Vue.js 的创始人 Evan You 开发的现代前端开发和构建工具,它提供了极快的冷启动、即时热模块替换和真正的按需编译。
当你使用 Vite 创建一个 Vue 项目时,@vitejs/plugin-vue
通常会被默认安装,因为它允许 Vite 正确处理 .vue
文件。
如何使用
安装
如果你正在创建一个新的 Vite + Vue 项目,你可以使用 Vite 的模板直接创建:
npm init @vitejs/app my-vue-app --template vue
如果你正在为一个现有的 Vite 项目添加 Vue 支持,你可以手动安装这个插件:
npm install --save-dev @vitejs/plugin-vue
配置
在你的 vite.config.js
文件中,你需要导入并使用这个插件:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
});
特性
CSS 提取:在生产构建中,这个插件可以将单文件组件中的 CSS 提取到单独的文件中。
自定义块支持:除了标准的 <template>
, <script>
, 和 <style>
块,你还可以在单文件组件中使用自定义块,并为其提供自定义的处理逻辑。
注意事项
确保你的 Vite 和 @vitejs/plugin-vue
的版本是兼容的。随着 Vite 和 Vue 的迭代,可能会有一些不兼容的更改,所以最好经常检查官方文档。
如果你还需要支持 Vue 2,你应该查看其他插件,如 vite-plugin-vue2
。
总的来说,@vitejs/plugin-vue
是 Vite 与 Vue 3 之间的桥梁,它使得在 Vite 项目中使用 Vue 变得简单且高效。
@vue/compiler-sfc
是 Vue 3 的一个官方包,用于处理 Vue 的单文件组件(Single File Components,简称 SFC)。这个包提供了解析、编译和优化 Vue SFC 的功能。它是 Vue CLI、Vite 和其他 Vue 工具背后的核心编译器。
以下是关于 @vue/compiler-sfc
的一些关键点:
1. 主要功能
.vue
文件解析为一个描述性对象,该对象包含模板、脚本、样式和其他自定义块。.vue
文件中的自定义块,如 <docs>
或 <i18n>
。2. API
@vue/compiler-sfc
提供了一些主要的 API 函数,如 parse
和 compileTemplate
。
例如,要解析一个 .vue
文件,你可以这样做:
import { parse } from "@vue/compiler-sfc";
const source = `
<template>
<div>Hello, Vue!</div>
</template>
`;
const { descriptor } = parse(source);
console.log(descriptor.template.content); // 输出: <div>Hello, Vue!</div>
3. 插件化
@vue/compiler-sfc
支持自定义插件,这使得开发者可以扩展或修改编译过程。
4. 与构建工具集成
构建工具,如 Webpack、Rollup 或 Vite,通常使用 @vue/compiler-sfc
来处理 .vue
文件。例如,Vue CLI 使用 vue-loader
来处理 .vue
文件,而 vue-loader
内部使用 @vue/compiler-sfc
。
5. <script setup>
@vue/compiler-sfc
也支持 Vue 3.2 引入的 <script setup>
语法糖,使得组合式 API 更加简洁。
总结
@vue/compiler-sfc
是 Vue 3 生态系统中的一个核心包,它处理 .vue
文件的解析和编译。如果你是一个 Vue 开发者,通常你不需要直接与这个包交互,但了解它的工作原理和功能可以帮助你更好地理解 Vue 的内部机制和构建过程。
在 Vue 单文件组件 (SFC) 中,<style>
标签的 scoped
属性用于确保样式只应用于当前组件,而不会影响到其他组件或全局样式。这为组件提供了一种局部的、封装的样式,使得组件更加独立和可重用。
如何工作?
当你为 <style>
标签添加 scoped
属性时,Vue 的编译过程会为该组件的模板中的每个 HTML 元素添加一个唯一的属性(例如 data-v-123abc
)。然后,它会在你的 CSS 选择器中添加相同的属性,从而确保这些样式只匹配那些带有相应属性的元素。
示例
考虑以下 Vue 组件:
<template>
<div class="example">
This is a scoped style example.
</div>
</template>
<style scoped>
.example {
color: blue;
}
</style>
在编译后,这个组件可能看起来像这样:
<div class="example" data-v-123abc>
This is a scoped style example.
</div>
<style>
.example[data-v-123abc] {
color: blue;
}
</style>
如你所见,.example
选择器现在只会匹配带有 data-v-123abc
属性的元素,这确保了样式的局部性。
注意事项
全局样式与局部样式:你可以在同一个 Vue SFC 中同时使用带有 scoped
属性的 <style>
和不带 scoped
属性的 <style>
。后者会定义全局样式。
子组件样式:scoped
样式不会影响子组件。如果你想为子组件定义样式,你需要使用更深的选择器或考虑使用其他策略,如 CSS 模块。
动态内容:由于 scoped
样式依赖于为模板元素添加特定的属性,因此它可能不适用于动态内容,如 v-html
指令插入的内容。
性能:虽然 scoped
样式提供了很好的封装性,但它也增加了选择器的复杂性。在大型应用中,过度使用 scoped
可能会对性能产生一些影响。
总的来说,<style scoped>
提供了一种简单的方法来封装 Vue 组件的样式,但在使用它时,你应该了解其工作原理和限制。
CSS Modules 是一种将样式封装到组件中的方法,它可以确保样式的局部作用域,避免全局样式的污染。在 Vue 中,你可以很容易地使用 CSS Modules。
如何在 Vue 中使用 CSS Modules
在单文件组件中声明
在你的 Vue 单文件组件中,你可以通过为 <style>
标签添加 module
属性来使用 CSS Modules:
This is a component
注意如何使用 $style.myClass
来引用样式。这是因为当你使用 CSS Modules 时,所有的类名都会被转换为局部作用域的,并且可以通过组件的 $style
对象来访问。
自定义类名转换
默认情况下,CSS Modules 会将类名转换为 camelCase。这意味着 .my-class
会变成 $style.myClass
。如果你想使用其他格式,你可以这样配置:
然后在模板中这样使用:
This is a component
与全局样式共存
你可以在同一个组件中同时使用 CSS Modules 和全局样式。只需为需要全局作用域的样式添加 global
属性:
优点
缺点
总的来说,CSS Modules 是 Vue 提供的一个强大的工具,它可以帮助你更好地管理和封装组件的样式。如果你正在寻找一种确保样式的局部作用域的方法,那么它绝对值得一试。
哈希值是一个固定长度的数字或字符序列,它是通过哈希函数从输入数据(通常是一个字符串或文件)计算得出的。哈希函数的设计目的是确保即使输入数据的微小变化也会导致输出的哈希值发生显著变化。哈希值有许多应用,包括数据检索、密码学、数据完整性验证等。
以下是关于哈希值的一些关键点:
固定长度:无论输入数据的大小或长度如何,哈希函数输出的哈希值都是固定长度的。
快速计算:对于给定的输入,哈希函数应该能够快速地计算出哈希值。
不可逆:从哈希值本身,不应该能够(至少在实际中)重新构造出原始的输入数据。
冲突最小化:虽然理论上不同的输入可能会产生相同的哈希值(称为“冲突”),但好的哈希函数会使这种情况的发生概率极低。
敏感性:即使是输入数据的微小变化,也应该导致输出哈希值的显著变化。
哈希值的应用:
数据结构:哈希表(或哈希映射)是一种使用哈希值来快速存储和检索数据的数据结构。
数据完整性:哈希值常用于验证数据的完整性。例如,下载文件时,网站可能会提供文件的哈希值,以便用户可以验证下载的文件是否完整且未被篡改。
密码学:在密码学中,哈希函数用于多种目的,如密码存储(存储哈希值而不是明文密码)和数字签名。
缓存:Web 浏览器和其他系统使用哈希值来确定数据是否已经在缓存中,从而避免不必要的数据重新获取或计算。
负载均衡:在分布式系统中,哈希值可以用于确定将请求发送到哪个服务器,确保请求均匀地分布在多个服务器上。
常见的哈希函数:
需要注意的是,随着技术的进步,一些早期的哈希函数(如 MD5 和 SHA-1)现在被认为是不安全的,因为它们容易受到攻击。在安全关键的应用中,建议使用更强大和经过时间验证的哈希函数,如 SHA-256 或 SHA-3。