1. Пишите впереди
1. Источник вдохновения
Я обычно люблю делать какие-то записи и сводки по некоторым вещам, включая некоторые компоненты.Когда накопленная сумма относительно велика, я считаю, что разрозненное накопление не подходит для управления.
Итак, я начал думать, как лучше управлять этими относительно разрозненными вещами более стандартизированным способом? Если вы управляете компонентами в виде библиотеки компонентов, будет ли она более подходящей для вашего собственного накопления и облегчения будущей работы?
Поэтому я начал ссылаться на некоторые отличные библиотеки компонентов пользовательского интерфейса на рынке, такие какelement-ui
,vux
,vant
д., прочитать его исходный код, понять устройство его архитектуры, а затем разобраться с набором собственной библиотеки компонентов мобильного UI.vui
.
В свободное время я активен в крупных технических сообществах, и часто некоторые друзья, которые какое-то время работали или все еще ищут стажировку, задают автору несколько вопросов: Как устроиться самому и сделать свой собственный фреймворк, колесо и библиотека? Как сделать библиотеку компонентов? Станет ли изюминкой вашего резюме создание библиотеки компонентов самостоятельно? Можете ли вы написать несколько статей по теме разработки библиотек компонентов? …
В духе ответов на вопросы и обмена ими родился этот пост в блоге.
2. Финальные визуализации
3. Обмен проблемами
Если у вас возникнут какие-либо вопросы при чтении статьи, вы можете присоединиться к дискуссионной группе для обсуждения (в дополнение к группе больших парней каждый день в группе также есть группа девушек~)
Фронта HodgePodge: 731175396
гитхаб:github.com/xuqiang521
Без дальнейших церемоний, давайте перейдем непосредственно к настоящей боевой главе~
2. Строительство окружающей среды
1. Создайте среду NODE
Здесь я говорю только об установке NODE под Mac и Windows.
I. Установка под Mac
-
Если у вас не установлен менеджер пакетов Mac
homebrew
Первый шаг — установить его/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-
использовать
homebrew
Установитьnode
brew install node
II. Установка под окном
window
непосредственно в окружающую средуофициальный сайт узлаЗагрузите соответствующую версию и нажмите «Далее», чтобы завершить установку.
После завершения установки проверьтеnode
а такжеnpm
Версия
node -v
# v9.6.1
npm -v
# 5.6.0
на твоем компьютереnode
Окружение настроено, теперь нам нужно установить скаффолдинг, от которого зависит библиотека компонентов.
2. Создайте проект vue
I. Установите vue-кли
# 全局安装
npm i -g vue-cli
# 查看vue-cli用法
vue -h
# 查看版本
vue -V
# 2.9.3
ii. Создайте проект с помощью vue-cli
использоватьvue-cli
изinit
Директива инициализируетpersonal-components-library
проект
# 项目基于 webpack
vue init webpack personal-components-library
При сборке строительные леса позволят вам заполнить некоторые описания и зависимости проекта.Пожалуйста, обратитесь к содержимому, которое я выбираю ниже, чтобы заполнить
# 项目名称
Project name? personal-components-library
# 项目描述
Project description? A Personal Vue.js components Library project
# 项目作者
Author? qiangdada
# 项目构建 vue 版本(选择默认项)
Vue build? standalone
# 是否下载 vue-router (后期会用到,这里选 Yes)
Install vue-router? Yes
# 是否下载 eslint (为了制定合理的开发规范,这个必填)
Use ESLint to lint your code? Yes
# 安装默认的标准 eslint 规则
Pick an ESLint preset? Standard
# 构建测试案例
Set up unit tests? Yes
# 安装 test 依赖 (选择 karma + mocha)
Pick a test runner? karma
# 构建 e2e 测试案例 (No)
Setup e2e tests with Nightwatch? No
# 项目初始化完是否安装依赖 (npm)
Should we run `npm install` for you after the project has been created? (recom
mended) npm
Как только вы выбрали, вы можете подождать,vue-cli
Это поможет вам собрать проект и установить зависимости.
Структура проекта инициализации следующая:
├── build webpack打包以及本地服务的文件都在里面
├── config 不同环境的配置都在这里
├── index.html 入口html
├── node_modules npm安装的依赖包都在这里面
├── package.json 项目配置信息
├── README.md 项目介绍
├── src 我们的源代码
│ ├── App.vue vue主入口文件
│ ├── assets 资源存放(如图片)
│ ├── components 可以复用的模块放在这里面
│ ├── main.js 入口js
│ ├── router 路由管理
└── webpack.config.js webpack配置文件
├── static 被copy的静态资源存放地址
├── test 测试文档和案例
если вы используетеnpm
Если зависимость загрузки слишком медленная или некоторые ресурсы заблокированы, рекомендуется использоватьcnpm
Сделать зависимые загрузки
# 全局安装 cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 使用 cnpm 进行依赖安装
cnpm i
После завершения установки вы можете начатьvue
проект~
npm run dev
затем посетитеhttp://localhost:8080
можно успешно получить доступ черезvue-cli
Строить изvue
Пункт, я уже установил среду разработки этой зависимости библиотеки.
3. Создайте новый каталог
Прежде всего, нам нужно уточнить цель этого раздела, нам нужно изменить каталог, чтобы лучше разработать библиотеку компонентов.
Мы уже построили его в предыдущем разделеvue
проект, но каталог инициализированного проекта не может соответствовать последующей разработке и обслуживанию библиотеки компонентов. Итак, что нам нужно сделать в этой главе, так это преобразовать инициализациюvue
Каталог проекта, превратите его в каталог, требуемый библиотекой компонентов, а затем давайте сделаем это.
1. Каталог библиотеки компонентов
- build: Этот каталог в основном используется для хранения файлов, связанных со сборкой.
- packages: Этот каталог в основном используется для хранения всех компонентов
-
examples: Этот каталог в основном используется для хранения отображения библиотеки компонентов.
demo
а также文档
все сопутствующие документы -
src: Этот каталог в основном используется для управления основной записью регистрации компонентов, инструментов,
mixins
и т.д. (для этого нужно преобразовать инициализированныйsrc
содержание) - test: этот каталог используется для хранения тестовых случаев (продолжайте использовать инициализированный каталог)
- lib: каталог, в котором упакована библиотека компонентов.
- .github: В качестве библиотеки компонентов с открытым исходным кодом, если вы хотите разрабатывать с другими, то этот каталог используется для хранения некоторых правил разработки, которые вы определяете сами, что тоже очень хорошо.
Хорошо, начните преобразовывать каталог вашего инициализированного проекта.
2. Запустите проект снова
I. Измените каталог примеров
Мы заранее знаем, что когда мы запускаем локальную службу, основным файлом записи страницы являетсяindex.html
. Теперь наш первый шаг — поговорить о главном входе на страницу.html
а такжеjs
перейти кexamples
ниже каталога.examples
Конкретный каталог выглядит следующим образом
├── assets css,图片等资源都在这
├── pages 路由中所有的页面
├── src
│ ├── components demo中可以复用的模块放在这里面
│ ├── index.js 入口js
│ ├── index.tpl 页面入口
│ ├── App.vue vue主入口文件
│ ├── router.config.js 路由js
Измененный код каждого файла выглядит следующим образом
-
index.js
import Vue from 'vue' import App from './App' import router from './router.config' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app-container', router, components: { App }, template: '<App/>' })
-
index.tpl
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>My Component Library</title> </head> <body> <div id="app-container"> <app></app> </div> </body> </html>
-
App.vue
<template> <div id="app"> <router-view/> </div> </template> <script> export default { name: 'App' } </script>
-
router.config.js
import Vue from 'vue' import Router from 'vue-router' import hello from '../pages/hello' // 请自行去pages下面创建一个hello.vue,以方便之后的测试 Vue.use(Router) export default new Router({ routes: [ { path: '/', component: hello } ] })
ii. Измените каталог src
src
Каталог в основном используется для хранения основного файла записи регистрации компонента, метода инструмента,mixins
и другие документы. мы сверхуexamples
Каталог можно узнать, оригиналsrc
Некоторые файлы в файле необходимо удалить.Измененный каталог выглядит следующим образом
├── mixins mixins方法存放在这
├── utils 一些常用辅助方法存放在这
├── index.js 组件注册主入口
iii. Измените некоторые упакованные файлы в каталоге сборки.
Думая о маленьких друзьях, которые это видят, они тоже должны знать, что нам нужно делать сейчас. Да, это изменить входной файл локальной службы. Если он может только работать, то изменитеentry
js запись в иhtml-webpack-plugin
На запись страницы можно ссылаться. Код такой (ставится только код ключа)
entry: {
'vendor': ['vue', 'vue-router'],
'vui': './examples/src/index.js'
},
// ...
plugins: [
// ...
// 将入口改成examples/src/index.tpl
new HtmlWebpackPlugin({
chunks: ['vendor', 'vui'],
template: 'examples/src/index.tpl',
filename: 'index.html',
inject: true
})
]
Хорошо, изменено. повторно выполнитьnpm run dev
, тогда ваш проект может работать под новым входным файлом
3. Используйте компоненты локально
В этом разделе нам нужно реализовать службу, которую мы запускаем локально, которую можно использоватьpackages
Компоненты ниже. Ниже мы разрабатываем простейшийhello
компоненты для объяснения
я вpackages
создать следующийhello
компоненты
Чтобы иметь хорошее ограничение, здесь мы ограничиваем: компонент должен иметь указанный каталог и имя файла для унифицированного управления перед началом записи.packages
Под содержаниемhello
Файлы под компонентом следующие
├── hello
│ ├── hello.vue
hello.vue
Содержание выглядит следующим образом
<template>
<div class="v-hello">
hello {{ message }}
</div>
</template>
<script>
export default {
name: 'v-hello',
props: {
message: String
}
}
</script>
II.src/index.js
зарегистрировать компонент
sec/index.js
Файл также упомянут выше, он в основном используется для управления регистрацией всех компонентов в нашей библиотеке компонентов.
import Hello from '../packages/hello'
const install = function (Vue) {
if (install.installed) return
Vue.component(Hello.name, Hello)
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
install,
Hello
}
III.examples/src/index.js
Ссылка в файле записи js
Далее мне нужно переделатьexamples
Напишите это в насhello
компоненты для ссылки
import vui from 'src/index.js'
// 完整引用
Vue.use(vui)
// 独立引用
const { Hello } = vui
Vue.component(Hello.name, Hello)
IV.examples/pages/hello.vue
Использовать напрямую
существуетexamples/pages
Нам нужно создать демонстрационный файл с тем же именем, что и имя компонента, и использовать компонент
<v-hello message="my component library"></v-hello>
Когда результат вашей операции будет таким же, как на картинке выше, поздравляю. Вы успешно сделали еще один шаг к развитию библиотеки компонентов~
Видя это, мне нужно, чтобы читатель сосредоточился на документе в соответствии с моими собственными предпочтениями (конечно, я также могу сослаться на демонстрацию, приведенную выше). Только так мы можем сделать так, чтобы наша библиотека компонентов была плавно разработана.
В следующем разделе мы оптимизируемbuild
Следующий файл пакета, и пусть каждый публикует свои разработанные компоненты вnpm
Официальный сайт делает вашу библиотеку компонентов более удобной для использования другими!
В-четвертых, преобразуйте файл пакета и опубликуйте пакет npm.
Как обычно, прежде чем мы начнем основную часть главы, нам нужно знать, что эта глава должна делать и почему.
-
Поскольку первоначальный проект строительных лесов предназначен для
build
Файл имеет только один централизованно упакованный файлwebpack.prod.conf.js
-
Чтобы в будущем лучше использовать нашу библиотеку компонентов, нам нужно извлечь все модули, соответствующие библиотеке компонентов, и упаковать их в
vui.js
В файле (назовите его как хотите), чтобы мы могли обращаться к нашей библиотеке компонентов следующими способами.import Vue from 'vue' import vui from 'x-vui' Vue.use(vui)
-
Нам также необходимо
examples
Связанные файлы упакованы и управляются, потому что мы должны разработать официальный веб-сайт документации библиотеки компонентов позже, и все соответствующие входы официального веб-сайта документации находятся вexamples
середина
1. Измените файл пакета сборки
I. Интеграция локальных служебных файлов
Мы можем видеть из проекта инициализации,build
относящийся к документуwebpack
Файлы следующие
├── webpack.base.conf.js 基础配置文件
├── webpack.dev.conf.js 本地服务配置文件
├── webpack.prod.conf.js 打包配置文件
├── webpack.test.conf.js 测试配置文件(这里先不做过多描述)
Первоначальная упаковкаoutput
Выходной каталогdist
, этот каталог является выходным каталогом всего проекта после упаковки, а не каталогом, требуемым нашей библиотекой компонентов. Поскольку это не то, что нам нужно, что нам нужно в нужном каталоге?
- Файл библиотеки компонента первичного входа в находу JS
lib/vui.js
(основной файл библиотеки компонентов js) - CSS-файл основной записи библиотеки компонентов
lib/vui-css/index.css
(Основной файл css библиотеки компонентов, мы не будем слишком подробно описывать упаковку css в этой главе, и в следующих главах будет объяснено отдельно) -
examples
упакованный файлexamples/dist
(Главный вход официального сайта более позднего документа)
Теперь, когда цель поставлена, следующее, что нам нужно сделать, это разобраться в соответствующихwebpack
файл пакета, как показано ниже
├── webpack.base.conf.js 基础配置文件(配置方面和webpack.dev.conf.js的配置进行部分整合)
├── webpack.dev.conf.js 本地服务配置文件(将纯配置文件进行对应的删减)
├── webpack.build.js 组件库入口文件打包配置文件(将webpack.prod.conf.js重命名)
├── webpack.build.min.js examples展示文件打包配置文件(新增文件)
1. webpack.base.conf.js
начать ремонтwebpack.base.conf.js
Перед файлом нам нужно понять, что должны делать два файла пакета.
-
webpack.build.js
: выходlib/vui.js
Основной файл библиотеки компонентов js, который будет использоватьсяwebpack.base.conf.js
а такжеwebpack.dev.conf.js
Связанная конфигурация -
webpack.build.min.js
: выходexamples/dist
Файлы, связанные с документом, будут использоватьсяwebpack.base.conf.js
а такжеwebpack.dev.conf.js
Связанная конфигурация
С двухwebpack
будут использоваться файлы пакетовwebpack.base.conf.js
а такжеwebpack.dev.conf.js
соответствующей конфигурации, то почему бы нам не интегрировать некоторые из тех же файлов вwebpack.base.conf.js
в файле? Цель ясна, тогда следуй за мной
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
// 文件入口
entry: {
'vendor': ['vue', 'vue-router'],
'vui': './examples/src/index.js'
},
// 输出目录
output: {
path: path.join(__dirname, '../examples/dist'),
publicPath: '/',
filename: '[name].js'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
// 此处新增了一些 alias 别名
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'src': resolve('src'),
'packages': resolve('packages'),
'lib': resolve('lib'),
'components': resolve('examples/src/components')
}
},
// 延用原先的大部分配置
module: {
rules: [
// 原先的配置...
// 整合webpack.dev.conf.js中css相关配置
...utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
]
},
// 延用原先的配置
node: {
// ...
},
devtool: config.dev.devtool,
// 整合webpack.dev.conf.js中的devServer选项
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
// 整合webpack.dev.conf.js中的plugins选项
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// 页面主入口
new HtmlWebpackPlugin({
chunks: ['manifest', 'vendor', 'vui'],
template: 'examples/src/index.tpl',
filename: 'index.html',
inject: true
})
]
}
2. webpack.dev.conf.js
Здесь нужно только интегрироватьwebpack.base.conf.js
Конфигурацию можно удалить, чтобы избежать дублирования кода.
'use strict'
const utils = require('./utils')
const config = require('../config')
const baseWebpackConfig = require('./webpack.base.conf')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
process.env.PORT = port
baseWebpackConfig.devServer.port = port
baseWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${baseWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(baseWebpackConfig)
}
})
})
webpack.base.conf.js
а такжеwebpack.dev.conf.js
После того, как оба файла будут скорректированы, повторно выполнитеnpm run dev
На приведенном выше рисунке показано, что ваш локальный служебный файл был успешно изменен, как и ожидалось.
II. Файл пакета реконструкции
1. webpack.build.js
Основная цель этого файла — объединить все связанные с компонентами файлы в библиотеке компонентов и вывести их.lib/vui.js
основной файл
'use strict'
const webpack = require('webpack')
const config = require('./webpack.base.conf')
// 修改入口文件
config.entry = {
'vui': './src/index.js'
}
// 修改输出目录
config.output = {
filename: './lib/[name].js',
library: 'vui',
libraryTarget: 'umd'
}
// 配置externals选项
config.externals = {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}
// 配置plugins选项
config.plugins = [
new webpack.DefinePlugin({
'process.env': require('../config/prod.env')
})
]
// 删除devtool配置
delete config.devtool
module.exports = config
2. webpack.build.min.js
Основная цель этого файла - открыть единый адрес пакета,examples
Связанные с выходным файломexamples/dist
Справочник (т. е. последующие документы, вход на официальный сайт)
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const webpackConfig = merge(baseWebpackConfig, {
output: {
chunkFilename: '[id].[hash].js',
filename: '[name].min.[hash].js'
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: false
},
sourceMap: false
}),
// extract css into its own file
new ExtractTextPlugin({
filename: '[name].[contenthash].css',
allChunks: true,
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
]
})
module.exports = webpackConfig
Когда у нас есть все эти файлы, последним шагом будет запись команды упаковки вpackage.json
изscripts
бинго
"scripts": {
"build:vui": "webpack --progress --hide-modules --config build/webpack.build.js && rimraf examples/dist && cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.build.min.js"
},
Выполнение заказа,npm run build:vui
, иди ты
На данный момент локальная служба и два файла пакета были преобразованы.npm
Использовать его ~
2. Опубликовать пакет npm
Обратите внимание, если у вас нет собственногоnpm
Если у вас есть учетная запись, перейдите наnpm
Зарегистрируйте аккаунт на официальном сайте,Нажмите здесь, чтобы войти на официальный сайт, чтобы зарегистрироваться, шаги регистрации относительно просты, я не буду здесь описывать слишком много, если у вас есть какие-либо вопросы, вы можете задать их мне в группе обсуждения
I. Начнем с самой простой демонстрации
mkdir qiangdada520-npm-test
cd qiangdada520-npm-test
# npm 包主入口js文件
touch index.js
# npm 包首页介绍(具体啥内容你自行写入即可)
touch README.md
npm init
# package name: (qiangdada520-npm-test)
# version: (1.0.0)
# description: npm test
# entry point: (index.js) index.js
# test command:
# git repository:
# keywords: npm test
# author: qiangdada
# license: (ISC)
Тогда ОК, он сгенерируетpackage.json
,следующим образом
{
"name": "qiangdada-npm-test",
"version": "1.0.0",
"description": "npm test",
"main": "index.js", // npm 包主入口js文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"npm",
"test"
],
"author": "qiangdada",
"license": "MIT"
}
Далее нам нужно подключить наш регистрационный номер локальноnpm
учетная запись
npm adduser
# Username: 填写你自己的npm账号
# Password: npm账号密码
# Email: (this IS public) 你npm账号的认证邮箱
# Logged in as xuqiang521 on https://registry.npmjs.org/. 连接成功
воплощать в жизньnpm publish
начать публикацию
npm publish
# + qiangdada-npm-test@1.0.0
на этот раз ты идешьnpm
Официальный сайт может искать и видеть, что вы только что выпустили хороший пакет ~
ii. Опубликуйте библиотеку компонентов
На данный момент в библиотеке компонентов мы написали самую простуюhello
компонент, но это не мешает нам опубликовать его наnpm
Официальный сайт и этапы публикации так же просты, как в приведенном выше примере.
Исправлятьpackage.json
Часть описания в файле
// npm 包js入口文件改为 lib/vui.js
"main": "lib/vui.js",
// npm 发布出去的包包含的文件
"files": [
"lib",
"src",
"packages"
],
// 将包的属性改为公共可发布的
"private": false,
Примечание, тестnpm
Когда пакет выпущен, помните каждый разpackage.json
серединаversion
Версия выше, чем в прошлый раз.
начать публикацию
# 打包,输出lib/vui.js
npm run build:vui
# 发布
npm publish
# + component-library-test@1.0.1
iii. Использование компонентов, которые мы публикуем в npm
Выберите vue Project локальное присутствие в проекте
npm i component-library-test
# or
cnpm i component-library-test
Регистрация компонентов в файле ввода проекта
import Vue from 'vue'
import vui from 'component-library-test'
Vue.use(vui)
использовать на странице
<v-hello message="component library"></v-hello>
Пока что мы успешно трансформировали локальный служебный файл, реализовали упаковку основного файла библиотеки компонентов и главного входа официального сайта документа и, наконец, научились им пользоваться.npm
Опубликовать проект.
В следующей главе яcss
Упаковка файла объяснить.
Пять, управление файлами css и упаковка
В предыдущем разделе мы уже упаковали файл js. Но для библиотеки компонентов нам нужно управлять не только файлами js, но и файлами css, чтобы обеспечить последующее использование библиотеки компонентов.
В этом разделе я опишу, какwebpack
Разумное использование в проектах на основе сборкиgulp
Отдельное управление пакетами для файлов css.
Прежде чем мы начнем, нам нужно определить две цели:
- Как управлять файлами css, связанными с компонентами в библиотеке компонентов, куда их поместить для единого управления и как их написать
- Как файл css будет упакован и как один компонент будет выводить соответствующий одиночный css
1. Управление файлами CSS
Чтобы облегчить управление, каждый раз, когда мы создаем новый компонент, нам нужно создать соответствующий файл css для управления стилем компонента, чтобы обеспечить единое управление.
я. каталог css
Здесь мы будем хранить все файлы css вpackages/vui-css
В каталоге конкретная структура выглядит следующим образом
├── src
│ ├── common 存放组件公用的css文件
│ ├── mixins 存放一些mixin的css文件
│ ├── index.css css主入口文件
│ ├── hello.css 对应hello组件的单一css文件
├── gulpfile.js css打包配置文件
├── package.json 相关的版本依赖
II. Как написать файл css
Перед тем, как мы приступим к написанию css компонента, нам необходимо уточнить несколько моментов:
- Когда пользователь вводит библиотеку компонентов и использует стиль в компоненте стиля, он не может конфликтовать с проектом разработки пользователя.
- В некоторых особых случаях пользователь может переопределить стиль компонента и изменить его более удобным образом.
Чтобы справиться с этими двумя ситуациями, я лично считаю, что лучший способ на рынке — это управлять компонентами с помощью одного CSS и использоватьbem
Напишите css. хочу знатьbem
студенты, нажмите на ссылку ниже
- BEM
- Что вы думаете о том, как БЭМ назван в CSS?(Это вопрос, заданный несколько лет назад, но в нем все еще есть ссылка)
Далее у нас есть простойhello
Компоненты, чтобы дать объяснение, прежде чем начать, наденьте егоhello.vue
Содержание
<template>
<div class="v-hello">
<p class="v-hello__message">hello {{ message }}</p>
</div>
</template>
<script>
export default {
name: 'v-hello',
props: {
message: String
}
}
</script>
существуетpackages/vui-css/src
Создано в каталогеhello.css
@b v-hello {
color: #fff;
transform: scale(1);
@e message {
background: #0067ED;
}
}
потом у главного входаindex.css
введен вhello.css
документ
@import './hello.css';
существуетexamples/src/index.js
Представьте стиль библиотеки компонентов в
import 'packages/vui-css/src/index.css'
но отhello.css
содержание мы можем видеть, что это типичноbem
Метод записи не может быть проанализирован нормально. Нам нужно ввести соответствующиеpostcss
пара плагиновbem
Разобрана грамматика. Здесь мы будем использовать饿了么团队
Развитыйpostcss-salad
пара плагиновbem
разбирается грамматика, а во-вторых, этоsass-like
style, вам также необходимо использовать плагин под названиемprecss
, сначала установите зависимости~
npm i postcss-salad precss -D
После установки зависимостей нам нужно создать новую в корневом каталоге проекта.salad.config.json
используется для настройкиbem
Правила, конкретные правила заключаются в следующем
{
"browsers": ["ie > 8", "last 2 versions"],
"features": {
"bem": {
"shortcuts": {
"component": "b",
"modifier": "m",
"descendent": "e"
},
"separators": {
"descendent": "__",
"modifier": "--"
}
}
}
}
Далее нам нужно инициализировать проект.postcssrc
используется в файлеpostcss-salad
а такжеprecss
плагин, как показано ниже
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-salad": require('./salad.config.json'),
"postcss-url": {},
"precss": {},
"autoprefixer": {},
}
}
ОК, в это время снова запустите проект, вы увидите, что css вступает в силу, как показано на рисунке.
2, упаковка файла css
Чтобы лучше управлять файлами css в библиотеке компонентов, пользователи также могут импортировать файлы css, соответствующие компонентам, когда они хотят импортировать только один или несколько компонентов в библиотеке компонентов. Итак, нам нужно упаковать файл css отдельно, здесь нам нужно использоватьgulp
Чтобы выполнить соответствующую операцию упаковки, прежде чем вы начнете получать информацию об упаковке, убедитесь, что вы установили ее глобально.gulp
, если нет, то установить
npm i gulp -g
# 查看版本
gulp -v
# CLI version 3.9.1
Далее посмотримpackages/vui-css/package.json
Какие зависимости нужно использовать в файле
{
"name": "vui-css",
"version": "1.0.0",
"description": "vui css.",
"main": "lib/index.css",
"style": "lib/index.css",
// 和组件发布一样,也需要指定目录
"files": [
"lib",
"src"
],
"scripts": {
"build": "gulp build"
},
"license": "MIT",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-cssmin": "^0.2.0",
"gulp-postcss": "^7.0.1",
"postcss-salad": "^2.0.1"
},
"dependencies": {}
}
Мы видим, что это на самом деле то же самое, что и зависимость от файла css в библиотеке компонентов, но она основана наgulp
изpostcss
плагин. начать настройкуgulpfile.js
Перед этим не забудьте выполнитьnpm i
Установить зависимости.
Далее приступаем к настройкеgulpfile.js
, подробности следующим образом
const gulp = require('gulp')
const postcss = require('gulp-postcss')
const cssmin = require('gulp-cssmin')
const salad = require('postcss-salad')(require('../../salad.config.json'))
gulp.task('compile', function () {
return gulp.src('./src/*.css')
// 使用postcss-salad
.pipe(postcss([salad]))
// 进行css压缩
.pipe(cssmin())
// 输出到 './lib' 目录下
.pipe(gulp.dest('./lib'))
})
gulp.task('build', ['compile'])
Теперь можно приступить к выполнениюgulp build
Команда упаковывает файл css. Конечно, чтобы облегчить и лучше выполнить команду упаковки, нам теперь понадобитсяpackage.json
Добавьте к нему команду сборки css, как показано ниже.
"scripts": {
"build:vui-css": "gulp build --gulpfile packages/vui-css/gulpfile.js && rimraf lib/vui-css && cp-cli packages/vui-css/lib lib/vui-css && rimraf packages/vui-css/lib"
}
воплощать в жизньnpm run build:vui-css
, поехали, файлы js и css финальной упакованной библиотеки компонентов показаны на рисунке ниже
Хорошо, теперь вы можете вводить компоненты и их стили по отдельности. Наконец, чтобы пользователи могли напрямую использовать css вашего компонента, не забудьте опубликовать его вnpm
Официальный сайт ~ Шаги следующие
# 进到vui-css目录
cd packages/vui-css
# 发布
npm publish
На данный момент мы завершили управление и отдельную упаковку файлов css, а также завершили единый вывод файлов css. Таким образом, мы можем лучше разрабатывать и управлять css-файлами библиотеки компонентов, и в то же время мы можем облегчить использование библиотеки компонентов!
6. Модульное тестирование
На данный момент мы построили новый каталог, необходимый для библиотеки компонентов, мы также изменили упаковку файлов js и css, и мы сделали существенную подготовку для разработки библиотеки компонентов, но нам все еще нужно сделать некоторые очень важная предварительная работа для облегчения разработки и обслуживания последующих компонентов библиотеки компонентов.
Для фронтенд-тестирования это важная ветвь фронтенд-инжиниринга, так как же мы можем упустить такой важный угол в нашей библиотеке компонентов? Для модульного тестирования в основном есть два типа
- TDD (Разработка через тестирование): Разработка через тестирование, ориентированная на выходные результаты.
- BDD (разработка, управляемая поведением): разработка, управляемая поведением, с упором на логику тестирования.
В этой главе я покажу вам, как использовать встроенную инициализацию на основе проекта.Karma
+ Mocha
Эти две платформы модульно тестируют компоненты в нашей библиотеке компонентов.
1. Введение в фреймворк
дляKarma
+ Mocha
Я полагаю, что эти два фреймворка не будут незнакомы большинству людей, подвергшихся модульному тестированию, но здесь я думаю, что необходимо открыть отдельный раздел, чтобы дать краткое введение в эти два фреймворка.
I. Структура кармы
- KarmaЭто инструмент управления процессом выполнения тестов JavaScript на основе Node.js (Test Runner).
- Karmaэто инструмент тестирования, который позволяет тестировать ваш код в среде браузера.
- KarmaСделайте так, чтобы ваш код автоматически запускался в нескольких браузерах, таких как chrome, firefox и т. д.
Чтобы компоненты в нашей библиотеке компонентов могли работать во всех основных веб-браузерах для тестирования, мы выбралиKarma. самое главное этоKarmaдаvue-cli
Рекомендуемая среда модульного тестирования. Если вы хотите узнать больше оKarmaвведение, см.официальный сайт кармы
2. Мокко Фреймворк
-
MochaЯвляется
simple
,flexible
,fun
тестовая среда -
MochaПоддерживает асинхронные тестовые случаи, такие как
Promise
-
MochaПокрытие кода поддержки
coverage
отчет об испытаниях - Mochaпозволяет вам использовать любую библиотеку утверждений, которую вы хотите, напримерchai,should.js(стиль BDD),expect.jsтак далее
-
Mochaпри условии
before()
,after()
,beforeEach()
, так же какafterEach()
Четыре функции хука, которые нам удобны для установки разных операций на разных этапах, чтобы лучше завершить наши тесты.
Здесь я представляюmocha
Три основных использования , иdescribe
Четыре функции крючка (жизненный цикл)
-
описать (имя модуля, функция):
describe
является вложенным, описываяпрецедентэто правильно или нетdescribe('测试模块的描述', () => { // .... });
-
**it(информация, функция):** a
it
Соответствует модульному тестуit('单元测试用例的描述', () => { // .... })
-
Использование библиотеки утверждений
expect(1 + 1).to.be.equal(2)
-
describe
жизненный циклdescribe('Test Hooks', function() { before(function() { // 在本区块的所有测试用例之前执行 }); after(function() { // 在本区块的所有测试用例之后执行 }); beforeEach(function() { // 在本区块的每个测试用例之前执行 }); afterEach(function() { // 在本区块的每个测试用例之后执行 }); // test cases });
хочу узнать большеmocha
Студенты, которые работают, могут щелкнуть ссылку ниже, чтобы просмотреть
2. Практика модульного тестирования
В приведенном выше разделе я дал вам краткое введение в среду тестирования, официально рекомендованную Vue.Karma
а такжеMocha
, я также надеюсь, что, увидев это, вы сможете получить простое представление о модульном тестировании и общих средах тестирования.
I. Модульное тестирование компонента приветствия
Прежде чем начнется фактическое модульное тестирование, давайте посмотримKarmaконфигурация, здесь мы смотрим непосредственноvue-cli
Леса инициализированыkarma.conf.js
Конфигурация в файле (я сделал комментарий для конкретного использования)
var webpackConfig = require('../../build/webpack.test.conf')
module.exports = function karmaConfig (config) {
config.set({
// 浏览器
browsers: ['PhantomJS'],
// 测试框架
frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
// 测试报告
reporters: ['spec', 'coverage'],
// 测试入口文件
files: ['./index.js'],
// 预处理器 karma-webpack
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
// webpack配置
webpack: webpackConfig,
// webpack中间件
webpackMiddleware: {
noInfo: true
},
// 测试覆盖率报告
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}
Далее мы приходим к своимhello
Компонент просто тестируется (пишется только один тест-кейс), вtest/unit/specs
новыйhello.spec.js
файл и напишите следующий код
import Vue from 'vue' // 导入Vue用于生成Vue实例
import Hello from 'packages/hello' // 导入组件
// 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite)
describe('Hello.vue', () => {
// 每个describe块应该包括一个或多个it块,称为测试用例(test case)
it('render default classList in hello', () => {
const Constructor = Vue.extend(Hello) // 获得Hello组件实例
const vm = new Constructor().$mount() // 将组件挂在到DOM上
// 断言:DOM中包含class为v-hello的元素
expect(vm.$el.classList.contains('v-hello')).to.be.true
const message = vm.$el.querySelector('.v-hello__message')
// 断言:DOM中包含class为v-hello__message的元素
expect(message.classList.contains('v-hello__message')).to.be.true
})
})
После того, как тестовый пример написан, следующим шагом является тестирование. воплощать в жизньnpm run test
, иди ты ~ , выводи результат
hello.vue
✓ render default classList in hello
2. Оптимизируйте модульные тесты
сверхуhello
Как видно из тестового экземпляра компонента, нам нужно создать экземпляр компонента как экземпляр Vue и иногда монтировать его в DOM.
const Constructor = Vue.extend(Hello)
const vm = new Constructor({
propsData: {
message: 'component'
}
}).$mount()
Если в будущем каждый компонент будет иметь несколько экземпляров модульного теста, этот метод написания приведет к раздуванию нашего окончательного теста.Здесь мы можем обратиться кelement
упакованныйИнструмент модульного тестирования util.js. Нам нужно инкапсулировать некоторые из методов, обычно используемых Vue в модульном тестировании.Ниже я перечислю некоторые из методов, предоставляемых инструментом.
/**
* 回收 vm,一般在每个测试脚本测试完成后执行回收vm。
* @param {Object} vm
*/
exports.destroyVM = function (vm) {}
/**
* 创建一个 Vue 的实例对象
* @param {Object|String} Compo - 组件配置,可直接传 template
* @param {Boolean=false} mounted - 是否添加到 DOM 上
* @return {Object} vm
*/
exports.createVue = function (Compo, mounted = false) {}
/**
* 创建一个测试组件实例
* @param {Object} Compo - 组件对象
* @param {Object} propsData - props 数据
* @param {Boolean=false} mounted - 是否添加到 DOM 上
* @return {Object} vm
*/
exports.createTest = function (Compo, propsData = {}, mounted = false) {}
/**
* 触发一个事件
* 注: 一般在触发事件后使用 vm.$nextTick 方法确定事件触发完成。
* mouseenter, mouseleave, mouseover, keyup, change, click 等
* @param {Element} elm - 元素
* @param {String} name - 事件名称
* @param {*} opts - 配置项
*/
exports.triggerEvent = function (elm, name, ...opts) {}
/**
* 触发 “mouseup” 和 “mousedown” 事件,既触发点击事件。
* @param {Element} elm - 元素
* @param {*} opts - 配置选项
*/
exports.triggerClick = function (elm, ...opts) {}
Далее мы будем использовать определенный метод инструмента тестирования, преобразованиеhello
Тестовый экземпляр компонента, который будетhello.spec.js
файл для трансформации
import { destroyVM, createTest } from '../util'
import Hello from 'packages/hello'
describe('hello.vue', () => {
let vm
// 测试用例执行之后销毁实例
afterEach(() => {
destroyVM(vm)
})
it('render default classList in hello', () => {
vm = createTest(Hello)
expect(vm.$el.classList.contains('v-hello')).to.be.true
const message = vm.$el.querySelector('.v-hello__message')
expect(message.classList.contains('v-hello__message')).to.be.true
})
})
сделай это сноваnpm run test
, выведите результат
hello.vue
✓ render default classList in hello
3. Более широкое использование модульных тестов
Выше мы представили использование статических суждений в модульном тестировании, а затем протестируем некоторые асинхронные варианты использования и некоторые интерактивные события. Перед тестированием нам нужно изменить нашhello
Код компонента выглядит следующим образом
<template>
<div class="v-hello" @click="handleClick">
<p class="v-hello__message">hello {{ message }}</p>
</div>
</template>
<script>
export default {
name: 'v-hello',
props: {
message: String
},
methods: {
handleClick () {
return new Promise((resolve) => {
resolve()
}).then(() => {
this.$emit('click', 'this is click emit')
})
}
}
}
</script>
Дальше будем тестироватьhello
Может ли компонент успешно передать информацию через Promiseemit
Выходите, тестовый пример выглядит следующим образом
it('create a hello for click with promise', (done) => {
let result
vm = createVue({
template: `<v-hello @click="handleClick"></v-hello>`,
methods: {
handleClick (msg) {
result = msg
}
}
}, true)
vm.$el.click()
// 断言消息是异步emit出去的
expect(result).to.not.exist
setTimeout(_ => {
expect(result).to.exist
expect(result).to.equal('this is click emit')
done()
}, 20)
})
перезапустить тест, выполнитьnpm run test
, выведите результат
hello.vue
✓ render default classList in hello
✓ create a hello for click with promise
До сих пор мы изучили конфигурацию модульного тестирования и некоторые общие способы использования. Если вам нужно узнать больше о модульном тестировании, перейдите по ссылке, которую я предоставил ранее, для более глубокого погружения.
Семь, разработка официального веб-сайта документа (1)
Друзья, следуйте за мной, чтобы применить первые 5 глав на практике и создать базовую основу для разработки нашего компонента. Далее я возьму вас всех, чтобы закончить официальный сайт документов с высокими важными компонентами в библиотеке компонентов.
Каждый должен знать, что у хорошего проекта с открытым исходным кодом должен быть официальный веб-сайт документа, поэтому, чтобы сделать нашу библиотеку пользовательского интерфейса также частью превосходного, у нас также должен быть собственный официальный веб-сайт документа.
Хороший официальный сайт документации должен делать две вещи.
- Разберите API собственного проекта с открытым исходным кодом, чтобы пользователям было удобнее его использовать
- Есть демо-пример, чтобы пользователи могли увидеть эффект онлайн
Поскольку в этом сообщении в блоге библиотека компонентов, которую я предлагаю вам разработать, адаптирована для мобильного терминала, как мы можем сделать так, чтобы на нашем официальном веб-сайте документации было как описание документа API, так и демонстрация примера мобильного терминала. Это требует от нас разработки двух наборов страниц для адаптации. Нам нужно сделать следующее:
- Документация по API компонента дисплея ПК
- Демонстрация компонента мобильного дисплея
- Маршруты генерируются динамически
Прежде чем приступить к реальному бою, давайте взглянем на структуру каталогов, которую нам нужно использовать в этой главе.
├── assets css,图片等资源都在这
├── dist 打包好的文件都在这
├── docs PC端需要展示的markdown文件都在这
├── pages 移动端所有的demo都在这
├── src
│ ├── components demo中可以复用的模块放在这里面
│ ├── index.tpl 页面入口
│ ├── is-mobile.js 判断设备
│ ├── index.js PC端主入口js
│ ├── App.vue PC端入口文件
│ ├── mobile.js 移动端端主入口js
│ ├── MobileApp.vue 移动端入口文件
│ ├── nav.config.json 路由控制文件
│ ├── router.config.js 动态注册路由
В этой главе вы в основном познакомитесь с преобразованием файлов уценки и адаптацией маршрутизации для различных устройств.
После того, как идея ясна, давайте продолжим нашу практику разработки официального веб-сайта документа!
1. Преобразование файлов уценки
Как вы можете видеть из каталога, который я указал выше, все файлы уценки хранятся в папке docs, и каждый файл уценки соответствует документации API компонента. В результате мы хотим преобразовать каждый файл уценки в документации, чтобы сделать его компонентом Vue, и зарегистрировать преобразованный компонент Vue в маршруте, чтобы он мог получить доступ к каждому файлу уценки через маршрут.
Для разбора файлов уценки в компоненты Vue на рынке есть много трех сторон.webpack
плагин, конечно если ты правwebpack
Если вы более опытны, вы также можете попробовать дать пощечину самостоятельно. Здесь я использую его напрямую饿了么团队
развитыйvue-markdown-loader.
I. Используйте загрузчик vue-markdown
Первый шаг — установка зависимостей
npm i vue-markdown-loader -D
Второй шаг, вwebpack.base.conf.js
используется в файлеvue-markdown-loader
{
test: /\.md$/,
loader: 'vue-markdown-loader',
options: {
// 阻止提取脚本和样式标签
preventExtract: true
}
}
Третий шаг, попробуйте попробовать. доdocs
добавить внутрьhello.md
файл, а затем написатьhello
Инструкции по использованию компонентов
## Hello
**Hello 组件,Hello 组件,Hello 组件,Hello 组件**
### 基本用法
```html
<template>
<div class="hello-page">
<v-hello message="my component library" @click="handleClick"></v-hello>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
name: 'hello',
data () {
return {
msg: ''
}
},
methods: {
handleClick (msg) {
this.msg = msg
}
}
}
</script>
```
### Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| message | 文本信息 | string | — | — |
### Events
| 事件名称 | 说明 | 回调参数 |
|---------- |-------- |---------- |
| click | 点击操作 | — |
Четвертый шаг, будетhello.md
зарегистрироваться на маршрут
route.push({
path: '/component/hello',
component: require('../docs/hello.md')
})
Наконец, посетите страницу. В это время можно найтиhello.md
Контент был преобразован в компоненты Vue, и к нему можно получить доступ через загрузку маршрута, но страница уродлива и уродлива ~ вот так
2. Добавьте тему и стиль выделения в md.
Конечно, мне нет нужды объяснять эту ситуацию, и все это могут знать. Да, проанализированный файл уценки такой уродливый, только потому, что мы не добавили тему выделения в наш файл уценки и не установили базовый стиль страницы документа. Итак, далее нам нужно добавить красивую тему выделения и чистые базовые стили в наши файлы уценки.
Для тем здесь мы будем использоватьhighlight.js
внутриatom-one-darkтема.
Первый шаг, установкаhighlight.js
npm i highlight -D
Второй шаг, вexamples/src/App.vue
Представьте тему, и для того, чтобы установить основной стиль документа, нам также нужно изменить макет App.vue.
<template>
<div class="app">
<div class="main-content">
<div class="page-container clearfix">
<div class="page-content">
<router-view></router-view>
</div>
</div>
</div>
</div>
</template>
<script>
import 'highlight.js/styles/atom-one-dark.css'
export default {
name: 'App'
}
</script>
Третий шаг — установить основной стиль документа. существуетassets
Чжунсинdocs.css
, напишите начальный стиль, так как объем кода слишком велик, он не будет размещен здесь. Каждый может копироватьdocs.cssВнутри кода для местногоdocs.css
файл, затем вexamples/src/index.js
импортировать в
import '../assets/docs.css'
Наконец, преобразуйте правила синтаксического анализа уценки,vue-markdown-loader
предоставилpreprocess
Интерфейс дает нам свободу действий.Далее давайте определим структуру анализируемого файла уценки.webpack.base.conf.js
записать в файл
// 定义辅助函数wrap,将<code>标签都加上名为'hljs'的class
function wrap (render) {
return function() {
return render.apply(this, arguments)
.replace('<code v-pre class="', '<code class="hljs ')
.replace('<code>', '<code class="hljs">')
}
}
// ...
{
test: /\.md$/,
loader: 'vue-markdown-loader',
options: {
preventExtract: true,
preprocess: function(MarkdownIt, source) {
// 为table标签加上名为'table'的class
MarkdownIt.renderer.rules.table_open = function() {
return '<table class="table">'
};
MarkdownIt.renderer.rules.fence = wrap(MarkdownIt.renderer.rules.fence);
return source;
}
}
}
Затем снова посетитеlocalhost:8080/#/component/hello
Итак, наш md-файл был успешно разобран на компоненты Vue, и у нас есть красивая тема выделения и простые базовые стили~
2. Адаптация маршрутизации в различных средах устройств
Как я уже говорил ранее, библиотека компонентов, разработанная в этой статье, адаптирована для мобильного терминала, поэтому нам нужно отобразить документ на стороне ПК, а демонстрацию — на мобильном терминале.
В этом разделе я проведу вас через адаптацию различных конечных маршрутов. Конечно, это не сложно, в основном с помощью веб-пакета для создания многостраничных функций, так как это сделать конкретно? Ладно, без лишних слов, приступим
I. Регистрация входного файла
Первый шаг — зарегистрировать файл записи js, вwebpack.base.conf.js
записать в файл
entry: {
// ...
'vui': './examples/src/index.js', // PC端入口js
'vui-mobile': './examples/src/mobile.js' // 移动端入口js
}
Второй шаг, вход на страницу регистрации, вwebpack.base.conf.js
записать в файл
plugins: [
// ...
// PC端页面入口
new HtmlWebpackPlugin({
chunks: ['manifest', 'vendor', 'vui'],
template: 'examples/src/index.tpl',
filename: 'index.html',
inject: true
}),
// 移动端页面入口
new HtmlWebpackPlugin({
chunks: ['manifest', 'vendor', 'vui-mobile'],
template: 'examples/src/index.tpl',
filename: 'mobile.html',
inject: true
})
]
ii. Оценка окружающей среды оборудования
Регистрация входного файла завершена, следующее, что нам нужно сделать, это определить среду устройства. Здесь я буду использоватьnavigator.userAgent
С помощью регулярных выражений мы можем определить, принадлежит ли среда, в которой работает наша библиотека компонентов, терминалу ПК или мобильному терминалу?
Первый шаг, вexamples/src/is-mobile.js
Напишите следующий код в файле
/* eslint-disable */
const isMobile = (function () {
var platform = navigator.userAgent.toLowerCase()
return (/(android|bb\d+|meego).+mobile|kdtunion|weibo|m2oapp|micromessenger|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i).test(platform) ||
(/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i).test(platform.substr(0, 4));
})()
// 返回设备所处环境是否为移动端,值为boolean类型
export default isMobile
Второй шаг, файл входа js на стороне ПКexamples/src/index.js
Запишите следующие правила суждения в
import isMobile from './is-mobile'
// 是否为生产环境
const isProduction = process.env.NODE_ENV === 'production'
router.beforeEach((route, redirect, next) => {
if (route.path !== '/') {
window.scrollTo(0, 0)
}
// 获取不同环境下,移动端Demo对应的地址
const pathname = isProduction ? '/vui/mobile' : '/mobile.html'
// 如果设备环境为移动端,则直接加载移动端Demo的地址
if (isMobile) {
window.location.replace(pathname)
return
}
document.title = route.meta.title || document.title
next()
})
Третий шаг — ввести файл ввода js на мобильной стороне.examples/src/mobile.js
Напишите правило принятия решений аналогично предыдущему шагу в
import isMobile from './is-mobile'
const isProduction = process.env.NODE_ENV === 'production'
router.beforeEach((route, redirect, next) => {
if (route.path !== '/') {
window.scrollTo(0, 0)
}
// 获取不同环境下,PC端对应的地址
const pathname = isProduction ? '/vui/mobile' : '/mobile.html'
// 如果设备环境不是移动端,则直接加载PC端的地址
if (!isMobile) {
window.location.replace(pathname)
return
}
document.title = route.meta.title || document.title
next()
})
Наконец-то идеальноexamples/src/mobile.js
Файл и вход на мобильную страницуMobileApp.vue
документ
существуетexamples/src/mobile.js
Напишите следующий код в
import Vue from 'vue'
import VueRouter from 'vue-router'
import MobileApp from './MobileApp'
import Vui from 'src/index'
import isMobile from './is-mobile.js'
import Hello from '../pages/hello.vue'
import 'packages/vui-css/src/index.css'
Vue.use(Vui)
Vue.use(VueRouter)
const isProduction = process.env.NODE_ENV === 'production'
const router = new VueRouter({
base: isProduction ? '/vui/' : __dirname,
routes: [{
path: '/component/hello',
component: Hello
}]
})
router.beforeEach((route, redirect, next) => {
if (route.path !== '/') {
window.scrollTo(0, 0)
}
const pathname = isProduction ? '/vui/' : '/'
if (!isMobile) {
window.location.replace(pathname)
return
}
document.title = route.meta.title || document.title
next()
})
new Vue({
el: '#app-container',
router,
components: { MobileApp },
template: '<MobileApp/>'
})
существуетMobileApp.vue
написать в
<template>
<div class="mobile-container">
<router-view></router-view>
</div>
</template>
Затем вы можете перейти в браузер, чтобы попробовать эффект и посмотреть, могут ли различные среды устройств отображать соответствующий контент~
На этом план, который мы составили в этой главе, выполнен. «Идеальное» преобразование файлов md и адаптация маршрутизации в различных средах устройств. На этом разработка официального сайта документа (Часть 1) подходит к концу.В следующей главе мы продолжим завершать оставшиеся работы по разработке официального сайта документа!
Восемь, задокументируйте разработку официального веб-сайта (ниже)
В предыдущей главе мы выполнили:
- Преобразование файлов уценки с красивыми темами и стилями выделения
- Адаптация официального веб-сайта документа в различных средах устройств
В этой главе мы улучшим детали официального веб-сайта документа и разработаем полный официальный веб-сайт документа.
1. Управление маршрутизацией
Из каталога, приведенного в предыдущей главе, мы можем узнать, что каталог docs используется для хранения файлов md, которые необходимо отобразить на ПК, а каталог pages используется для хранения демо-файлов для мобильных устройств. Итак, как заставить компонент отображать соответствующие файлы в разных средах устройств (файл md, соответствующий компоненту дисплея ПК, и файл vue, соответствующий компоненту мобильного дисплея)? В таком случае, как мы можем разумно управлять маршрутизацией нашей библиотеки компонентов? Далее мы продолжаем следующие разработки по этим вопросам. обязательно пригодится здесьis-mobile.js
Перейдите, чтобы определить среду оборудования, следуйте за мной, чтобы медленно выполнять конкретную работу.
Первый шаг, вexamples/src
новый файл подnav.config.json
файл, напишите следующее
{
// 为了之后组件文档多语言化
"zh-CN": [
{
"name": "Vui 组件",
"showInMobile": true,
"groups": [
{
// 管理相同类型下的所有组件
"groupName": "基础组件",
"list": [
{
// 访问组件的相对路径
"path": "/hello",
// 组件描述
"title": "Hello"
}
]
}
]
}
]
}
Второй шаг – улучшитьrouter.config.js
файл, измените его на вспомогательную функцию для регистрации маршрута
const registerRoute = (navConfig, isMobile) => {
let route = []
// 目前只有中文版的文档
let navs = navConfig['zh-CN']
// 遍历路由文件,逐一进行路由注册
navs.forEach(nav => {
if (isMobile && !nav.showInMobile) {
return
}
if (nav.groups) {
nav.groups.forEach(group => {
group.list.forEach(nav => {
addRoute(nav)
})
})
} else if (nav.children) {
nav.children.forEach(nav => {
addRoute(nav)
})
} else {
addRoute(nav)
}
})
// 进行路由注册
function addRoute (page) {
// 不同的设备环境引入对应的路由文件
const component = isMobile
? require(`../pages${page.path}.vue`)
: require(`../docs${page.path}.md`)
route.push({
path: '/component' + page.path,
component: component.default || component
})
}
return route
}
export default registerRoute
Третий шаг, основная запись js файла на стороне ПКexamples/src/index.js
и JS-файл мобильной основной записиexamples/src/mobile.js
Внутри зарегистрированного маршрута напишите следующий код
import registerRoute from './router.config'
import navConfig from './nav.config'
const routesConfig = registerRoute(navConfig)
const router = new VueRouter({
routes: routesConfig
})
Тогда посетите официальный сайт нашей текущей библиотеки компонентов.
2. Отображение API ПК
Из окончательных визуализаций в предыдущей главе мы видим, что сторона ПК разделена на три части, а именно:
- Шапка, краткое описание библиотеки компонентов и ссылка на гитхаб проекта
- Левая колонка, маршрутизация компонентов и отображение заголовка
- Правая колонка, отображение документации API компонента
Далее приступим к завершению отображения API на стороне ПК
я голова
Голова относительно проста, нам нужно толькоexamples/src/components
под новымpage-header.vue
файл, напишите следующее
<template>
<div class="page-header">
<div class="page-header__top">
<h1 class="page-header__logo">
<a href="#">Vui.js</a>
</h1>
<ul class="page-header__navs">
<li class="page-header__item">
<a href="/" class="page-header__link">组件</a>
</li>
<li class="page-header__item">
<a href="https://github.com/Brickies/vui" class="page-header__github" target="_blank"></a>
</li>
<li class="page-header__item">
<span class="page-header__link"></span>
</li>
</ul>
</div>
</div>
</template>
Для конкретных стилей, пожалуйста, посетите непосредственноpage-header.vueсмотреть
2. Левая колонка
В левом столбце мы отображаем маршрутизацию компонентов и заголовки. На самом деле это правильноexamples/src/nav.config.json
Анализировать и показывать.
мы вexamples/src/components
под новымside-nav.vue
файл, нормальная структура файла выглядит следующим образом
<li class="nav-item">
<a href="javascript:void(0)">Vui 组件</a>
<div class="nav-group">
<div class="nav-group__title">基础组件</div>
<ul class="pure-menu-list">
<li class="nav-item">
<router-link
active-class="active"
:to="/component/hello"
v-text="navItem.title">Hello
</router-link>
</li>
</ul>
</div>
</li>
Но мы сейчас собираемсяexamples/src/nav.config.json
Для анализа улучшенный код выглядит следующим образом
<li class="nav-item" v-for="item in data">
<a href="javascript:void(0)" @click="handleTitleClick(item)">{{ item.name }}</a>
<template v-if="item.groups">
<div class="nav-group" v-for="group in item.groups">
<div class="nav-group__title">{{ group.groupName }}</div>
<ul class="pure-menu-list">
<template v-for="navItem in group.list">
<li class="nav-item" v-if="!navItem.disabled">
<router-link
active-class="active"
:to="base + navItem.path"
v-text="navItem.title" />
</li>
</template>
</ul>
</div>
</template>
</li>
Полный код нажмите здесьside-nav.vue
iii. App.vue
мы пишем нашpage-header.vue
а такжеside-nav.vue
Два файла находятся вApp.vue
используется в
<template>
<div class="app">
<page-header></page-header>
<div class="main-content">
<div class="page-container clearfix">
<side-nav :data="navConfig['zh-CN']" base="/component"></side-nav>
<div class="page-content">
<router-view></router-view>
</div>
</div>
</div>
</div>
</template>
<script>
import 'highlight.js/styles/atom-one-dark.css'
import navConfig from './nav.config.json'
import PageHeader from './components/page-header'
import SideNav from './components/side-nav'
export default {
name: 'App',
components: { PageHeader, SideNav },
data () {
return {
navConfig: navConfig
}
}
}
</script>
Затем снова зайдите на страницу, результат будет таким, как показано на рисунке.
3. Мобильная демонстрация
Принципы демо-версии для мобильных устройств и версии для ПК схожи, и их необходимо анализировать.nav.config.json
файл для отображения
I. Компоненты мобильной домашней страницы
В настоящее время помимо главной страницы входа нашего мобильного терминалаMobileApp.vue
Кроме того, нет зависимости от компонента корневого каталога.Далее мы сначала завершим разработку компонента корневого каталога.examples/src/components
под новымdemo-list.vue
файл, написать что-нибудь
<template>
<div class="side-nav">
<h1 class="vui-title"></h1>
<h2 class="vui-desc">VUI 移动组件库</h2>
</div>
</template>
Затем нам нужно сослаться на него в маршруте, вmobile.js
записать в файл
import DemoList from './components/demo-list.vue'
routesConfig.push({
path: '/',
component: DemoList
})
Затем приступайте к совершенствованиюdemo-list.vue
документ
<template>
<div class="side-nav">
<h1 class="vui-title"></h1>
<h2 class="vui-desc">VUI 移动组件库</h2>
<div class="mobile-navs">
<div v-for="(item, index) in data" :key="index">
<div class="mobile-nav-item" v-if="item.showInMobile">
<mobile-nav v-for="(group, s) in item.groups" :group="group" :base="base" :key="s"></mobile-nav>
</div>
</div>
</div>
</div>
</template>
<script>
import navConfig from '../nav.config.json';
import MobileNav from './mobile-nav';
export default {
data() {
return {
data: navConfig['zh-CN'],
base: '/component'
};
},
components: {
MobileNav
}
};
</script>
<style lang="postcss">
.side-nav {
width: 100%;
box-sizing: border-box;
padding: 90px 15px 20px;
position: relative;
z-index: 1;
.vui-title,
.vui-desc {
text-align: center;
font-weight: normal;
user-select: none;
}
.vui-title {
padding-top: 40px;
height: 0;
overflow: hidden;
background: url(https://raw.githubusercontent.com/xuqiang521/vui/master/src/assets/logo.png) center center no-repeat;
background-size: 40px 40px;
margin-bottom: 10px;
}
.vui-desc {
font-size: 14px;
color: #666;
margin-bottom: 50px;
}
}
</style>
Здесь мы цитируемmobile-nav.vue
файл, который также является компонентом отображения мобильного демонстрационного списка, который мы завершим далее.
ii. навигационный список
существуетexamples/src/components
под новымmobile-nav.vue
файл, разборnav.config.json
файл для отображения демонстрационного списка.
<template>
<div class="mobile-nav-group">
<div
class="mobile-nav-group__title mobile-nav-group__basetitle"
:class="{
'mobile-nav-group__title--open': isOpen
}"
@click="isOpen = !isOpen">
{{group.groupName}}
</div>
<div class="mobile-nav-group__list-wrapper" :class="{ 'mobile-nav-group__list-wrapper--open': isOpen }">
<ul class="mobile-nav-group__list" :class="{ 'mobile-nav-group__list--open': isOpen }">
<template v-for="navItem in group.list">
<li
class="mobile-nav-group__title"
v-if="!navItem.disabled">
<router-link
active-class="active"
:to="base + navItem.path">
<p>
{{ navItem.title }}
</p>
</router-link>
</li>
</template>
</ul>
</div>
</div>
</template>
<script>
export default {
props: {
group: {
type: Object,
default: () => {
return [];
}
},
base: String
},
data() {
return {
isOpen: false
};
}
};
</script>
Затем напишите стиль списка
<style lang="postcss">
@component-namespace mobile {
@b nav-group {
border-radius: 2px;
margin-bottom: 15px;
background-color: #fff;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
@e basetitle {
padding-left: 20px;
}
@e title {
font-size: 16px;
color: #333;
line-height: 56px;
position: relative;
user-select: none;
@m open {
color: #38f;
}
a {
color: #333;
display: block;
user-select: none;
padding-left: 20px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
&:active {
background: #ECECEC;
}
> p {
border-top: 1px solid #e5e5e5;
}
}
}
@e list-wrapper {
height: 0;
overflow: hidden;
@m open {
height: auto;
}
}
@e list {
transform: translateY(-50%);
transition: transform .2s ease-out;
@m open {
transform: translateY(0);
}
}
li {
list-style: none;
}
ul {
padding: 0;
margin: 0;
overflow: hidden;
}
}
}
</style>
Далее, повторно посетитеhttp://localhost:8080/mobile.html, вы можете получить ожидаемые результаты без каких-либо сюрпризов
Итак, наша «грубая» полка библиотеки компонентов построена.
Сообщение в блоге почти закончено, и весь код в статье размещен.github
Выше я напишу статью в будущем, чтобы помочь вам постепенно улучшить некоторые детали в нашей библиотеке компонентов, чтобы наша библиотека компонентов могла быть более совершенной.
адрес гитхаба:GitHub.com/Сюй Цян521/…
Очередная волна рекламы в конце статьи ~~~
Группа внешней связи: 731175396