Я обычно люблю записывать и суммировать некоторые вещи, включая некоторые компоненты. Когда я накапливаю их много, я обнаружил, что рассеянное накопление больше не подходит для управления.
Итак, я начал думать, есть ли хороший способ управлять этими более разбросанными вещами относительно стандартизированным образом? Если компоненты управляются в форме компонентных библиотек, будет ли это более подходящим для вашего собственного накопления и облегчить будущую работу?
Поэтому я начал ссылаться на некоторые превосходные библиотеки компонентов пользовательского интерфейса на рынке, такие как element-ui , vux , vant и т. Д., Прочитайте исходный код, чтобы понять построение ее архитектуры, а затем разобрать набор моей собственной библиотеки компонентов мобильных пользовательских интерфейсов vui .
В свободное время я активно участвую в крупных технических сообществах. У меня часто есть друзья, которые какое -то время работали или все еще готовятся найти стажировку, чтобы задать автору несколько вопросов: как урегулировать себя и сделать свою собственную структуру, колесо и библиотеку? Как сделать библиотеку компонентов? Станет ли библиотека компонентов самой основной момент вашего резюме? Можете ли вы написать несколько статей о разработке библиотеки компонентов? ...
Этот пост родился в настроении отвечать на сомнения и обмениваться вопросами.
Если у вас есть какие -либо вопросы при чтении статей, присоединяйтесь к дискуссионной группе, чтобы обсудить (в дополнение к группе крупных парней, разговаривающих каждый день, есть и группа девушек ~)
Передняя мешалка: 731175396
GitHub: https://github.com/xuqiang521
Без лишних слов, давайте перейдем прямо в фактическую боевую главу ~
Здесь я расскажу только об установке узла под Mac и окном
Если вы еще не установили homebrew
/usr/bin/ruby -e " $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install ) " Установите node , используя homebrew
brew install node Если вы хотите перейти на официальный веб -сайт window , чтобы загрузить соответствующую версию, нажмите «Следующий шаг», чтобы завершить установку.
После завершения установки проверьте версии node и npm
node -v
# v9.6.1
npm -v
# 5.6.0 С тех пор была построена среда node на вашем компьютере. Затем нам нужно установить библиотеку компонентов для создания лесов зависимостей.
# 全局安装
npm i -g vue-cli
# 查看vue-cli用法
vue -h
# 查看版本
vue -V
# 2.9.3 Инициализировать проект под названием « vue-cli personal-components-library init
# 项目基于 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 , построенного через vue-cli . На этом этапе была установлена среда разработки, от которой зависит ваша библиотека компонентов.
Прежде всего, нам нужно прояснить цель этого раздела. Нам нужно изменить каталог, чтобы лучше разработать библиотеки компонентов.
Мы уже создали проект vue в предыдущем разделе, но инициализированный каталог проекта не может соответствовать последующей разработке и обслуживанию библиотеки компонентов. Поэтому в этой главе мы должны сделать преобразование каталога инициализированного проекта vue и превратить его в каталог, необходимый библиотекой компонентов. Давайте принять меры дальше.
demo и文档компонентных библиотекmixins и т. Д. Регистрации компонентов (для этого нам необходимо преобразовать инициализированный каталог src )Хорошо, начните преобразовать каталог проектов, которые вы инициализировали.
Из предыдущего примера мы знаем, что когда мы запускаем локальную службу, основным файлом записи страницы является 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
}
]
} ) Справочник src в основном используется для хранения основных входных файлов, методов инструментов, mixins и других файлов для зарегистрированного компонента. Из приведенного выше каталога examples мы можем знать, что некоторые файлы в исходном src должны быть удалены. Модифицированный каталог заключается в следующем
├── mixins mixins方法存放在这
├── utils 一些常用辅助方法存放在这
├── index.js 组件注册主入口 Подумайте об этом, когда вы видите это, вы также должны знать, что нам нужно делать сейчас. Правильно, это просто изменять файл входа локальной службы. Если можно запустить, то измените запись JS в entry и ссылку на запись страницы 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 One One, и ваш проект может работать в соответствии с новым файлом входа
В этом разделе нам нужно реализовать сервис, который мы начали локально, который может использовать компоненты ниже packages . Давайте разработаем самый простой компонент hello , чтобы объяснить это
hello в packages Чтобы иметь хороший обязательный характер, здесь мы ограничиваем: перед началом написания компонента, указанный каталог и имя файла должны управляться равномерно. Файлы под компонентом hello в каталоге packages следующие
├── hello
│ ├── hello.vue hello.vue Content - это следующее
< template >
< div class =" v-hello " >
hello {{ message }}
</ div >
</ template >
< script >
export default {
name : 'v-hello' ,
props : {
message : String
}
}
</ script > 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
} examples/src/index.js inpit js file Далее мне нужно ссылаться на компонент hello который мы написали в модифицированных examples в предыдущем разделе
import vui from 'src/index.js'
// 完整引用
Vue . use ( vui )
// 独立引用
const { Hello } = vui
Vue . component ( Hello . name , Hello ) examples/pages/hello.vue В examples/pages нам нужно создать демо -файл с тем же именем, что и имя компонента, и использовать компонент
< v-hello message =" my component library " > </ v-hello >Если ваш результат бега такой же, как и картина выше, то поздравляю. Вы успешно сделали еще один шаг к разработке компонентных библиотек ~
Увидев это, мне нужны все читатели, чтобы центрально управлять файлами в соответствии с их собственными предпочтениями (конечно, вы также можете обратиться к демонстрации, которую я дал выше). Только таким образом последующая работа по разработке нашей библиотеки компонентов может быть гладкой.
В следующем разделе мы оптимизируем упакованные файлы ниже build и отправим вас в публикацию разработанных компонентов на официальный веб -сайт npm , чтобы ваша библиотека компонентов могла использоваться более удобно!
Как обычно, до начала текста главы мы должны знать, что нужно сделать в этой главе и почему.
Поскольку первоначальный проект лесов имеет только один упакованный файл в центре для build webpack.prod.conf.js
Для того, чтобы наша библиотека компонентов лучше использовалась в будущем, нам необходимо извлечь все модули, соответствующие библиотеке компонентов, в файл vui.js (что вам нравится имя), чтобы мы могли обратиться к нашей библиотеке компонентов следующим образом.
import Vue from 'vue'
import vui from 'x-vui'
Vue . use ( vui ) Нам также необходимо упаковать и управлять соответствующими файлами в examples , потому что мы должны разработать официальный документ официальный веб -сайт библиотеки компонентов позже, а соответствующие входы официального веб -сайта документа находятся в examples
Из проекта инициализации мы видим, что файл webpack в файле build следующим образом
├── webpack.base.conf.js 基础配置文件
├── webpack.dev.conf.js 本地服务配置文件
├── webpack.prod.conf.js 打包配置文件
├── webpack.test.conf.js 测试配置文件(这里先不做过多描述) Инициализированный output каталог выходного выходного сигнала является dist . Этот каталог является выходным каталогом после упаковки всего проекта, а не каталога, требуемого нашей библиотекой компонентов. Поскольку это не то, что мы хотим, что мы хотим сделать в каталоге, который нам нужен?
lib/vui.js (основной файл библиотеки компонентов JS)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.jswebpack.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 .
Приведенная выше картина, по -видимому, указывает на то, что ваш локальный файл обслуживания был успешно изменен, как и ожидалось ~
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 = config2. webpack.build.min.js
Основная цель этого файла состоит в том, чтобы открыть один адрес пакета и вывести соответствующие файлы в examples в examples/dist Directory (то есть официальный вход на веб -сайт последующего документа)
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 Когда мы собираем все эти файлы вместе, последний шаг - написать команду пакета на scripts package.json .
"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 , Go
На этом этапе локальная служба и два упакованных файла были преобразованы. Давайте попробуем использовать npm ~
Обратите внимание, что если у вас нет собственной учетной записи npm , пожалуйста, зарегистрируйте учетную запись самостоятельно на официальном веб -сайте npm . Нажмите здесь, чтобы ввести официальный веб -сайт, чтобы зарегистрироваться. Шаги регистрации относительно просты. Я не буду описать это здесь больше. Если у вас есть какие -либо вопросы, вы можете спросить меня в группе WeChat.
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
# + [email protected] В настоящее npm вы можете искать и увидеть только что выпущенный пакет ~
В настоящее время мы написали самый простой компонент hello в библиотеке компонентов, но это вообще не влияет на нашу публикацию на официальном веб -сайте npm , и шаги публикации так же просты, как приведенный выше пример.
Измените некоторые описания в файле package.json
// npm 包js入口文件改为 lib/vui.js
"main" : "lib/vui.js" ,
// npm 发布出去的包包含的文件
"files" : [
"lib" ,
"src" ,
"packages"
] ,
// 将包的属性改为公共可发布的
"private" : false , Обратите внимание, что при тестировании пакет npm выпускается, помните, что version в package.json выше предыдущей.
Начните публикацию
# 打包,输出lib/vui.js
npm run build:vui
# 发布
npm publish
# + [email protected] Выберите локальный проект Vue и введите проект
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 в библиотеке компонентов.
В предыдущем разделе мы уже упаковали файл JS. Однако для библиотек компонентов, что нам нужно сделать, это не только управлять файлом JS, но и управлять файлом CSS, чтобы обеспечить последующее использование библиотеки компонентов.
В этом разделе я расскажу о том, как разумно использовать gulp для разделения упаковки и управления файлами CSS в проектах на основе строительства webpack .
Прежде чем мы начнем, нам нужно уточнить две цели:
Чтобы облегчить управление, каждый раз, когда мы создаем новый компонент, нам необходимо создать соответствующий файл CSS для управления стилем компонента и достижения отдельного управления.
Здесь мы будем хранить все файлы CSS в каталоге packages/vui-css . Конкретная структура заключается в следующем
├── src
│ ├── common 存放组件公用的css文件
│ ├── mixins 存放一些mixin的css文件
│ ├── index.css css主入口文件
│ ├── hello.css 对应hello组件的单一css文件
├── gulpfile.js css打包配置文件
├── package.json 相关的版本依赖Прежде чем начать писать компоненты CSS, нам нужно уточнить некоторые моменты:
Я лично думаю, что лучший способ на рынке - управлять компонентами в одном CSS и написать CSS с помощью bem . Если вы хотите узнать bem , нажмите на ссылку ниже
Далее, давайте объясним простой компонент 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 > Создать hello.css в packages/vui-css/src
@b v-hello {
color : # fff ;
transform : scale ( 1 );
@e message {
background : # 0067ED ;
}
} Затем импортируйте файл hello.css в главном входном index.css
@import './hello.css' ; Представление стилей библиотеки компонентов в examples/src/index.js
import 'packages/vui-css/src/index.css' Но из контента hello.css мы видим, что это типичный метод написания bem и не может быть проанализирован нормально. Нам нужно представить соответствующий плагин postcss , чтобы проанализировать синтаксис bem . Здесь мы будем использовать плагин postcss-salad разработанный饿了么团队чтобы проанализировать синтаксис bem . Во-вторых, этот файл CSS sass-like также должен использовать плагин с именем precss . Сначала установите зависимости.
npm i postcss-salad precss -D После завершения установки зависимостей нам необходимо создать salad.config.json в каталоге Project Root для настройки правил bem . Конкретные правила следующие
{
"browsers" : [ " ie > 8 " , " last 2 versions " ],
"features" : {
"bem" : {
"shortcuts" : {
"component" : " b " ,
"modifier" : " m " ,
"descendent" : " e "
},
"separators" : {
"descendent" : " __ " ,
"modifier" : " -- "
}
}
}
} Далее нам нужно использовать плагины postcss-salad и precss в инициализированном файле .postcssrc , следующим образом
module . exports = {
"plugins" : {
"postcss-import" : { } ,
"postcss-salad" : require ( './salad.config.json' ) ,
"postcss-url" : { } ,
"precss" : { } ,
"autoprefixer" : { } ,
}
}Хорошо, когда вы снова запустите проект в настоящее время, вы увидите, что 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 в библиотеке компонентов, за исключением того, что это плагин postcss на основе gulp . Прежде чем начать настроить 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. Конечно, для того, чтобы облегчить и лучше выполнять команды упаковки, нам теперь нужно добавить команду CSS Build в package.json в корневом каталоге проекта, следующим образом
"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 библиотеки компонентов, а также возможность облегчить использование библиотеки компонентов!
До сих пор мы создали новый каталог, необходимый для библиотеки компонентов, и мы также преобразовали упаковку файлов JS и файлов CSS. Мы сделали достаточную подготовку к разработке библиотеки компонентов, но нам все еще нужно сделать очень важную предварительную работу, чтобы облегчить разработку и поддержание последующих компонентов библиотеки компонентов.
Для тестирования на переднем крае это важная ветвь фронтальной инженерии. Поэтому как такая важная часть можно пропустить в нашей библиотеке компонентов? Для модульных тестов есть в основном два типа
В этой главе я Karma вас к компонентам модуля в нашей библиотеке компонентов, используя две структуры, основанные на инициализации Mocha .
Я считаю, что большинство людей, которые подвергались воздействию модульных тестов, знакомы с двумя рамками, Karma + Mocha , но здесь я думаю, что необходимо открыть отдельный раздел, чтобы дать краткое представление о двух рамках.
Чтобы позволить компонентам в нашей библиотеке компонентов работать в основных основных веб -браузерах для тестирования, мы выбрали карму . Самое главное, что карма -это модульная структура тестирования, рекомендованная vue-cli . Если вы хотите узнать больше о карме , пожалуйста, проверьте официальный веб -сайт Karma
simple , flexible , fun структура тестированияPromisecoverage кодаbefore() , after() , beforeEach() и afterEach() , чтобы мы могли устанавливать различные операции на разных этапах, чтобы лучше завершить наши тесты. Здесь я представлю три основных использования mocha , а также четыре функции describe (жизненный цикл)
Опишите (Modulename, функция): describe врожден, описывая, является ли тестовый пример правильным.
describe ( '测试模块的描述' , ( ) => {
// ....
} ) ; ** IT (info, function): ** Один из 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 , могут нажать на ссылку ниже, чтобы просмотреть ее
В приведенном выше разделе я кратко представляю тестовые рамки Karma и Mocha , которые официально рекомендуются Vue. Я также надеюсь, что, когда вы увидите это, вы можете просто понять модульные тестирование и общие рамки тестирования.
Перед началом фактического модульного теста давайте посмотрим на конфигурацию кармы . Здесь мы напрямую рассмотрим конфигурацию в файле karma.conf.js , инициализированной каркасом vue-cli (я прокомментировал конкретное использование)
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 (напишите только один тестовый пример), создайте новый файл hello.spec.js в test/unit/specs и напишите следующий код
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 Из приведенного выше экземпляра hello компонента нам нужно создать экземпляр компонента в экземпляр Vue, и иногда нам нужно установить его на DOM
const Constructor = Vue . extend ( Hello )
const vm = new Constructor ( {
propsData : {
message : 'component'
}
} ) . $mount ( ) Если у каждого компонента есть несколько экземпляров модульного тестирования позже, это написание приведет к раздумке нашего окончательного теста. Здесь мы можем обратиться к модульному тестовому инструменту util.js, инкапсулированным element . Нам нужно инкапсулировать некоторые часто используемые методы в модульном тестировании. Ниже я перечислю некоторые методы, представленные в инструменте.
/**
* 回收 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 Выше мы представили использование единичных испытаний на статических суждениях. Затем мы протестируем некоторые асинхронные варианты использования и некоторые интерактивные события. Перед тестированием нам нужно немного изменить код нашего компонента 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 успешно emit информацию через обещание. Например, тестовый пример заключается в следующем
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На этом этапе мы изучили конфигурацию модульных тестов и некоторые часто используемые использование. Если вам нужно узнать больше о модульном тестировании, перейдите по ссылке, которую я предоставил ранее, чтобы перейти на более глубокое обучение
Друзья, следуйте за мной, чтобы практиковать предыдущие 5 глав и создали основную полку для разработки компонентов. Далее я возьму вас с вами, чтобы закончить официальный веб -сайт документа с высокими важными компонентами в библиотеке компонентов.
Каждый должен знать, что хорошие проекты с открытым исходным кодом должны иметь официальные веб -сайты документации, поэтому, чтобы сделать нашу библиотеку пользовательского интерфейса одной из лучших, мы также должны использовать наш собственный официальный веб -сайт документации.
Хороший официальный веб -сайт документа требует двух вещей.
Поскольку библиотека компонентов, которую я привел вас к разработке, подходит для мобильных устройств, как мы можем сделать наш официальный веб -сайт документов иметь как описания документации 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 动态注册路由Эта глава в основном заставляет вас реализовать преобразование файлов разметки и адаптацию маршрутизации различных устройств.
После разъяснения идей давайте продолжим развивать наш официальный веб -сайт документов!
Из каталога, который я дал выше, мы видим, что папка DOCS хранится в файле Markdown, и каждый файл разметки соответствует документу API компонента. Результатом, который мы хотим, является преобразование каждого файла разметки в документах, чтобы превратить его в компоненты VUE и зарегистрировать конвертированные компоненты VUE в маршрут, чтобы он мог получить доступ к каждому файлу разметки через маршрут.
Для анализа файлов разметки в компоненты VUE на рынке есть много трехпартийных плагинов webpack . Конечно, если у вас есть глубокое понимание webpack , вы также можете попытаться выбрать один самостоятельно. Здесь я напрямую использую Vue-Markdown-Loader, разработанный饿了么团队
Первый шаг - полагаться на установку
npm i vue-markdown-loader -D Второй шаг-использовать vue-markdown-loader в файле webpack.base.conf.js
{
test : / .md$ / ,
loader : 'vue-markdown-loader' ,
options : {
// 阻止提取脚本和样式标签
preventExtract : true
}
} Третий шаг - попробовать. Сначала добавьте файл hello.md в docs , а затем напишите инструкции для использования компонентов 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 | 点击操作 | — | Шаг 4: Зарегистрируйтесь hello.md в маршруте
route . push ( {
path : '/component/hello' ,
component : require ( '../docs/hello.md' )
} ) Наконец, посетите страницу. В настоящее время вы можете обнаружить, что содержание hello.md было преобразовано в компонент VUE и может быть доступен с помощью загрузки маршрутизации, но страница уродливая ~ Точно так же
Конечно, мне не нужно объяснять эту ситуацию, вы тоже можете это знать. Да, файл Parsed Markdown настолько уродлив, просто потому, что мы не выделили ни тему для нашего файла Markdown, ни установили базовый стиль страницы документа. Итак, затем нам нужно добавить хорошую выделенную тему и чистый базовый стиль в наш файл разметки.
Что касается тем, здесь мы будем использовать тему Atom-One-Dark в highlight.js .
Первый шаг - установить 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 > Третий шаг - установить основной стиль документа. Создайте новые docs.css в assets и напишите начальный стиль. Поскольку объем кода слишком велик, я не буду публиковать его здесь. Вы можете самостоятельно скопировать код в 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 и имеет красивую выделенную тему и простой базовый стиль ~
Как я уже говорил, библиотека компонентов, разработанная этой статьей, адаптирована для мобильных устройств, поэтому нам нужно отобразить документы на ПК и демонстрации на мобильном телефоне.
В этом разделе я возьму вас на адаптацию маршрутов к разным концам. Конечно, эта штука не сложно. В основном он использует WebPack для создания многостраничных функций. Так как это сделать конкретно? Хорошо, давай начнем прямо сейчас
Первый шаг - зарегистрировать файл записи 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
} )
] Регистрация файла входа завершена, и то, что нам нужно сделать дальше, это определить среду устройства. Здесь я буду использовать navigator.userAgent с регулярными выражениями, чтобы определить, принадлежит ли среда, в которой работает наша библиотека компонентов к стороне ПК или мобильной стороны?
Первый шаг-написать следующий код в examples/src/is-mobile.js -файл
/* eslint-disable */
const isMobile = ( function ( ) {
var platform = navigator . userAgent . toLowerCase ( )
return ( / (android|bbd+|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 Второй шаг состоит в том, чтобы написать следующие правила суждения в 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) заканчивается здесь. В следующей главе мы продолжим завершать оставшуюся работу по разработке официального сайта документа!
В предыдущей главе мы завершили:
В этой главе мы улучшим детали официального веб -сайта документа и разработаем полный официальный документ.
Из каталога, приведенного в предыдущей главе, мы можем знать, что каталог документов используется для хранения файлов MD, которые необходимо отобразить ПК, и каталог страниц используется для хранения мобильных демонстрационных файлов. Итак, как компоненты могут отображать соответствующие файлы в разных средах устройств (сторона ПК отображает файлы MD, соответствующие компонентам, а мобильная сторона отображает файлы VUE, соответствующие компонентам)? Как мы можем разумно управлять маршрутизацией нашей библиотеки компонентов в этом случае? Далее мы продолжаем разработку ниже, основываясь на этих вопросах. is-mobile.js определенно будет использоваться здесь для определения среды устройства. Пожалуйста, следуйте за мной, чтобы выполнить конкретную работу.
Первый шаг - создать новый файл файла nav.config.json в examples/src и написать следующий контент
{
// 为了之后组件文档多语言化
"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 File examples/src/index.js основного портального файла js examples/src/mobile.js основного портального файла JS/src/mobile.js с мобильной стороны и написать следующий код
import registerRoute from './router.config'
import navConfig from './nav.config'
const routesConfig = registerRoute ( navConfig )
const router = new VueRouter ( {
routes : routesConfig
} )Затем посетите наш текущий документ библиотеки компонентов Официальный веб -сайт
Из окончательных визуализаций предыдущей главы мы видим, что терминал ПК разделен на три части, а именно:
Далее, давайте начнем отображать API ПК
Заголовок относительно прост. Нам нужно только создать новый файл page-header.vue в examples/src/components и написать следующий контент
< 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 >Для конкретных стилей, пожалуйста, посетите страницу header.vue непосредственно, чтобы просмотреть их.
С левой стороны мы показываем компонентные маршруты и названия. На самом деле, это будет анализ и отображать examples/src/nav.config.json .
Мы создаем новый файл side-nav.vue в examples/src/components . Нормальная структура файла заключается в следующем
< 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
Мы используем 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 >Затем посетите страницу еще раз, результат, как показано на картинке
Принципы мобильной демонстрации и ПК похожи. Оба должны проанализировать файл nav.config.json для отображения
В настоящее время, за исключением основной входной страницы MobileApp.vue , наш мобильный терминал не имеет зависимости корневых компонентов. Далее мы сначала заполним разработку корневого компонента, создадим новый файл demo-list.vue в examples/src/components и написать несколько контента
< 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文件,这也是我们接下来要完成的移动端Demo 列表展示组件
在examples/src/components下新建mobile-nav.vue文件,解析nav.config.json文件,从而进行Demo 列表展示。
< 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地址:https://github.com/xuqiang521/personal-component-library
文章末尾再打一波广告~~~
前端交流群:731175396
美团点评长期招人,如果有兴趣的话,欢迎一起搞基,简历投递方式交流群中有说明~
小伙伴们你们还在等什么呢?赶紧先给文章点波赞,然后关注我一波,然后加群和大佬们一起交流啊~~~