用过 vue-cli
脚手架搭建 vue 项目都知道,vue-cli
中内置了 vue-style-loader
去加载样式模块,最后通过 标签把样式加载到页面,但是
style-loader
同样可以达到同样的效果,那么 vue 官方为啥还封装一个 vue-style-loader
库呢?那么它们到底有啥区别?平时项目中又该如何选择呢?
为了更好的去分析这两个库,我们简单的搭建一个 vue 项目。
其实如果对 webpack 很熟悉的话,从 0 开始搭建一个 vue 项目就很简单了,所以强烈推荐大家去看一下我上一篇 webpack 的文章,来和 webpack 谈场恋爱吧。
首先创建一个目录叫 style-loader-demo
,然后初始化 npm
:
mkdir style-loader-demo && cd style-loader-demo && npm init
接下来我们需要安装 webpack 相关的依赖:
webpack 核心库。
在工程目录 style-loader-demo
执行以下命令安装 webpack:
npm install -D webpack --registry https://registry.npm.taobao.org
webpack 指令库。
在工程目录 style-loader-demo
执行以下命令:
npm install -D webpack-cli --registry https://registry.npm.taobao.org
webpack 开发者服务框架。
在工程目录 style-loader-demo
执行以下命令:
npm install -D webpack-dev-server --registry https://registry.npm.taobao.org
webpack 配置工具。
在工程目录 style-loader-demo
执行以下命令:
npm install -D webpack-chain --registry https://registry.npm.taobao.org
在工程目录 style-loader-demo
下创建一个 webpack.config.js
文件:
touch webpack.config.js
然后对 webpack.config.js
进行配置,用 webpack-chain
导入一个 webpack 配置:
const config = new (require('webpack-chain'))();
module.exports = config.toConfig();
为了开发方便,我们在 package.json
中声明两个脚本 build
跟 dev
:
{
"name": "style-loader-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rimraf dist && webpack --mode=production",
"dev": "webpack-dev-server --mode=development --progress"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^5.0.0",
"html-webpack-plugin": "^4.5.0",
"style-loader": "^2.0.0",
"vue-style-loader": "^4.1.2",
"webpack": "^4.44.2",
"webpack-chain": "^6.5.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"vue": "^2.6.12",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12"
}
}
我们首先在工程目录 style-loader-demo
下创建一个 src
目录,然后在 src
目录下创建一个 main.s
文件:
mkdir src && cd src && touch main.js && cd ..
然后我们找到 webpack.config.js
文件,对 webpack 的入口和出口进行配置:
const path = require('path');
const config = new (require('webpack-chain'))();
config
.context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
.entry('app') // 入口文件名称为 app
.add('./src/main.js') // 入口文件为 ./src/main.ts
.end()
.output
.path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
.filename('[name].[hash:8].js') // 打包出来的 bundle 名称为 "[name].[hash:8].js"
.publicPath('/') // publicpath 配置为 "/"
.end()
vue 核心 API。
npm install vue --registry https://registry.npm.taobao.org
.vue
文件加载器。
npm install vue-loader --registry https://registry.npm.taobao.org
.vue
文件模版解析器。
npm install vue-template-compiler --registry https://registry.npm.taobao.org
npm install -D html-webpack-plugin --registry https://registry.npm.taobao.org
接下来我们在工程目录 sy_webpack-wedding
底下创建一个 public
目录,然后在 public
目录下创建一个 index.html
文件作为我们 app 的入口页面:
mkdir public && touch public/index.html
然后将以下内容写入 public/index.html
:
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Titletitle>
head>
<body>
<noscript>your browser should support javascript!noscript>
<div id="app">div>
body>
html>
css 模块加载器。
npm install -D css-loader --registry https://registry.npm.taobao.org
npm install -D vue-style-loader --registry https://registry.npm.taobao.org
npm install -D style-loader --registry https://registry.npm.taobao.org
webpack 配置全部内容:
const path = require('path');
const config = new (require('webpack-chain'))();
config
.context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
.entry('app') // 入口文件名称为 app
.add('./src/main.js') // 入口文件为 ./src/main.ts
.end()
.output
.path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
.filename('[name].[hash:8].js') // 打包出来的 bundle 名称为 "[name].[hash:8].js"
.publicPath('/') // publicpath 配置为 "/"
.end()
.resolve
.extensions
.add('.js')
.add('.vue') // 配置以 .js 等结尾的文件当模块使用的时候都可以省略后缀
.end()
.end()
.module
.rule('vue') // vue-loader 相关配置
.test(/\.vue$/) // 匹配 .vue 文件
.use('vue-loader')
.loader('vue-loader')
.end()
.end()
.rule('css') // css-loader 相关配置
.test(/\.css$/)
.use('vue-style-loader')
.loader('vue-style-loader')
.end()
.use('css-loader')
.loader('css-loader')
.options({
esModule: false
})
.end()
.end()
.end()
.plugin('vue-loader-plugin') // vue-loader 必须要添加 vue-loader-plugin
.use(require('vue-loader').VueLoaderPlugin, [])
.end()
.plugin('html') // 添加 html-webpack-plugin 插件
.use(require('html-webpack-plugin'), [
{
template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
chunks: ['app'], // 指定需要加载的 chunk
inject: 'body', // 指定 script 脚本注入的位置为 body
},
])
.end()
.devServer
.host('0.0.0.0') // 服务器外部可访问
.disableHostCheck(true) // 关闭白名单校验
.contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
.historyApiFallback({
disableDotRule: true, // 禁止在链接中使用 "." 符号
rewrites: [
{
from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
],
})
.port(8080) // 当前端口号
.hot(true) // 打开页面热载功能
.sockPort('location') // 设置成平台自己的端口
.open(true);
module.exports = config.toConfig();
我们在 src
目录下创建一个 app.vue
文件:
touch src/app.vue
然后将以下内容写入其中:
{
{ msg }}
很简单,就是一个简单的 vue 文件。
然后在 main.js
中引入 app.vue 文件:
import Vue from 'vue';
import App from './app.vue';
new Vue({
el: '#app',
render: (h) => h(App),
});
ok!一切准备完毕后,我们直接在工程目录运行 npm run dev
命令:
npm run dev
跟不上的童鞋可以直接下载源码:https://gitee.com/vv_bug/style-loader-demo
官网地址:https://github.com/vuejs/vue-style-loader
This is a fork based on style-loader. Similar to
style-loader
, you can chain it aftercss-loader
to dynamically inject CSS into the document as style tags. However, since this is included as a dependency and used by default invue-loader
, in most cases you don’t need to configure this loader yourself.
意思大概是:“基于 style-loader fork 过来的,跟 style-loader 类似。”
官网解释的还是比较敷衍的,我们结合 Demo 继续往下看。
看一下现在的设置,我们找到 webpack.config.js
:
const path = require('path');
const config = new (require('webpack-chain'))();
config
.context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
.entry('app') // 入口文件名称为 app
.add('./src/main.js') // 入口文件为 ./src/main.ts
.end()
.output
.path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
.filename('[name].[hash:8].js') // 打包出来的 bundle 名称为 "[name].[hash:8].js"
.publicPath('/') // publicpath 配置为 "/"
.end()
.resolve
.extensions
.add('.js')
.add('.vue') // 配置以 .js 等结尾的文件当模块使用的时候都可以省略后缀
.end()
.end()
.module
.rule('vue') // vue-loader 相关配置
.test(/\.vue$/) // 匹配 .vue 文件
.use('vue-loader')
.loader('vue-loader')
.end()
.end()
.rule('css') // css-loader 相关配置
.test(/\.css$/)
.use('vue-style-loader')
.loader('vue-style-loader')
.end()
.use('css-loader')
.loader('css-loader')
.options({
esModule: false
})
.end()
.end()
.end()
.plugin('vue-loader-plugin') // vue-loader 必须要添加 vue-loader-plugin
.use(require('vue-loader').VueLoaderPlugin, [])
.end()
.plugin('html') // 添加 html-webpack-plugin 插件
.use(require('html-webpack-plugin'), [
{
template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
chunks: ['app'], // 指定需要加载的 chunk
inject: 'body', // 指定 script 脚本注入的位置为 body
},
])
.end()
.devServer
.host('0.0.0.0') // 服务器外部可访问
.disableHostCheck(true) // 关闭白名单校验
.contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
.historyApiFallback({
disableDotRule: true, // 禁止在链接中使用 "." 符号
rewrites: [
{
from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
],
})
.port(8080) // 当前端口号
.hot(true) // 打开页面热载功能
.sockPort('location') // 设置成平台自己的端口
.open(true);
module.exports = config.toConfig();
再看一下现在的样式,我们找到 src/app.vue
文件:
上面的样式到底是怎样起作用的呢?
首先经过 vue-loader 处理一遍变成了这样:
.app[data-v-5ef48958] {
color: red;
}
可以看到,我们样式经过 vue-loader 处理后加上了一个 “data-v-5ef48958”,这是为什么呢?因为我们在 style 标签上加了 scoped 标记:
上面的样式到底是怎样起作用的呢?
首先经过 vue-loader 处理一遍变成了这样:
.app[data-v-5ef48958] {
color: red;
}
可以看到,我们样式经过 vue-loader 处理后加上了一个 “data-v-5ef48958”,这是为什么呢?因为我们在 style 标签上加了 scoped 标记: