J'aime généralement enregistrer et résumer certaines choses, y compris certains composants. Lorsque j'en accumule beaucoup, je trouve que l'accumulation dispersée ne convient plus à la gestion.
J'ai donc commencé à penser, y a-t-il un bon moyen de gérer ces choses plus dispersées de manière relativement standardisée? Si les composants sont gérés sous forme de bibliothèques de composants, sera-t-il plus adapté à votre propre accumulation et facilitera-t-elle les travaux futurs?
J'ai donc commencé à faire référence à d'excellentes bibliothèques de composants d'interface utilisateur sur le marché, telles que element-ui , vux , vant , etc., lisez le code source pour comprendre la construction de son architecture, puis j'ai réglé un ensemble de ma propre bibliothèque de composants d'interface utilisateur mobile vui .
Dans mes temps libres, je suis actif dans les grandes communautés techniques. J'ai souvent des amis qui ont travaillé pendant un certain temps ou qui se préparent toujours à trouver un stage pour poser quelques questions à l'auteur: comment vous régler et créer votre propre cadre, roue et bibliothèque? Comment faire une bibliothèque de composants? Faire vous-même une bibliothèque de composants deviendra-t-il le point culminant de votre CV? Pouvez-vous écrire des articles sur le développement de la bibliothèque des composants? ...
Ce billet de blog est né dans l'humeur de répondre aux doutes et de partager des questions.
Si vous avez des questions lorsque vous lisez des articles, veuillez rejoindre le groupe de discussion pour discuter (en plus d'un groupe de gros gars qui parlent tous les jours, il y a aussi un groupe de filles ~)
Hodgepodge frontal: 731175396
github: https://github.com/xuqiang521
Sans plus tarder, allons directement au chapitre de combat réel ~
Ici, je ne parlerai que de l'installation de nœud sous Mac et de la fenêtre
Si vous n'avez pas installé le Mac Package Manager homebrew , la première étape consiste à l'installer d'abord
/usr/bin/ruby -e " $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install ) " Installez node à l'aide homebrew
brew install node Si vous souhaitez vous rendre sur le site officiel window pour télécharger la version correspondante, cliquez sur Étape Suivant pour terminer l'installation.
Une fois l'installation terminée, vérifiez node et npm
node -v
# v9.6.1
npm -v
# 5.6.0 Depuis lors, l'environnement node sur votre ordinateur a été construit. Ensuite, nous devons installer la bibliothèque de composants pour créer l'échafaudage de dépendance.
# 全局安装
npm i -g vue-cli
# 查看vue-cli用法
vue -h
# 查看版本
vue -V
# 2.9.3 Initialiser un projet nommé personal-components-library à l'aide de la directive init vue-cli
# 项目基于 webpack
vue init webpack personal-components-libraryLors de la construction, l'échafaudage vous demandera de remplir certaines descriptions et dépendances du projet. Veuillez vous référer au contenu que j'ai sélectionné ci-dessous pour le remplir.
# 项目名称
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 Après l'avoir sélectionné, vous pouvez attendre. vue-cli vous aidera à créer le projet et à effectuer une installation de dépendance.
La structure du projet d'initialisation est la suivante:
├── 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 测试文档和案例Si vous utilisez npm pour télécharger trop lentement les dépendances ou si certaines ressources sont clôturées, il est recommandé d'utiliser cnpm pour télécharger les dépendances
# 全局安装 cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 使用 cnpm 进行依赖安装
cnpm i Vous pouvez démarrer votre projet vue une fois l'installation de dépendance terminée ~
npm run dev Visitez ensuite http://localhost:8080 et vous pouvez accéder avec succès au projet vue construit via vue-cli . À ce stade, l'environnement de développement dont dépend votre bibliothèque de composants a été installé.
Tout d'abord, nous devons clarifier l'objectif de cette section. Nous devons modifier le répertoire afin de mieux développer des bibliothèques de composants.
Nous avons déjà construit le projet vue dans la section précédente, mais le répertoire du projet initialisé ne peut pas répondre au développement et à la maintenance ultérieurs d'une bibliothèque de composants. Par conséquent, ce que nous devons faire dans ce chapitre est de transformer le répertoire du projet vue initialisé et de le transformer en répertoire nécessaire à la bibliothèque de composants. Passons à l'action ensuite.
demo et文档des bibliothèques de composantsmixins , etc. de l'enregistrement des composants (pour cela, nous devons transformer le répertoire src initialisé)OK, commencez à transformer le répertoire des projets que vous avez initialisés.
À partir de l'exemple précédent, nous savons que lorsque nous démarrons le service local, le fichier d'entrée principal de la page est index.html . Maintenant, notre première étape consiste à déplacer l'entrée principale de la page html et js vers le répertoire examples . Le répertoire examples spécifiques est le suivant
├── assets css,图片等资源都在这
├── pages 路由中所有的页面
├── src
│ ├── components demo中可以复用的模块放在这里面
│ ├── index.js 入口js
│ ├── index.tpl 页面入口
│ ├── App.vue vue主入口文件
│ ├── router.config.js 路由jsLe code modifié de chaque fichier est le suivant
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 est principalement utilisé pour stocker les fichiers d'entrée principaux, les méthodes d'outils, mixins et autres fichiers pour le composant enregistré. Nous pouvons savoir dans le répertoire examples ci-dessus que certains fichiers du src d'origine doivent être supprimés. Le répertoire modifié est le suivant
├── mixins mixins方法存放在这
├── utils 一些常用辅助方法存放在这
├── index.js 组件注册主入口 Pensez-y, lorsque vous voyez cela, vous devriez également savoir ce que nous devons faire maintenant. C'est vrai, c'est juste pour modifier le fichier d'entrée du service local. S'il est juste possible d'exécuter, modifiez l'entrée JS dans entry et la référence d'entrée de page de html-webpack-plugin . Le code est le suivant (seuls les codes clés sont placés)
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
} )
] Ok, modifié. Réexécutez npm run dev une fois, et votre projet peut s'exécuter sous le nouveau fichier d'entrée
Dans cette section, ce que nous devons implémenter, c'est le service que nous avons commencé localement, qui peut utiliser les composants ci-dessous packages . Développer le composant hello le plus simple pour l'expliquer
hello dans packages Afin d'avoir une bonne nature contraignante, nous restreignions ici: avant de commencer à écrire un composant, un répertoire et un nom de fichier spécifié doivent être gérés uniformément. Les fichiers sous hello dans packages sont les suivants
├── hello
│ ├── hello.vue hello.vue le contenu est le suivant
< 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 est également mentionné ci-dessus. Il est principalement utilisé pour gérer l'enregistrement de tous les composants dans notre bibliothèque de composants
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 Entrée JS Fichier Ensuite, j'ai besoin de référencer le composant hello que nous avons écrit dans les examples modifiés dans la section précédente
import vui from 'src/index.js'
// 完整引用
Vue . use ( vui )
// 独立引用
const { Hello } = vui
Vue . component ( Hello . name , Hello ) examples/pages/hello.vue Dans examples/pages nous devons créer un fichier de démonstration avec le même nom que le nom du composant et utiliser le composant
< v-hello message =" my component library " > </ v-hello >Si votre résultat en cours d'exécution est le même que l'image ci-dessus, félicitations. Vous avez réussi une autre étape vers le développement de bibliothèques de composants ~
Après avoir vu cela, j'ai besoin de tous les lecteurs pour gérer les fichiers centralement en fonction de leurs propres préférences (bien sûr, vous pouvez également vous référer à la démo que j'ai donnée ci-dessus). Ce n'est que de cette manière que les travaux de développement ultérieurs de notre bibliothèque de composants peuvent être lisses.
Dans la section suivante, nous optimiserons les fichiers emballés ci-dessous build et vous emmènerons pour publier vos composants développés sur npm , afin que votre bibliothèque de composants puisse être utilisée plus facilement!
Comme d'habitude, avant le début du texte du chapitre, nous devons savoir ce qui doit être fait dans ce chapitre et pourquoi.
Depuis le projet initial d'échafaudage n'a qu'un seul fichier emballé centralement pour build de fichiers webpack.prod.conf.js
Pour que notre bibliothèque de composants soit mieux utilisée à l'avenir, nous devons extraire tous les modules correspondant à la bibliothèque de composants dans un fichier vui.js (ce que vous aimez le nom) afin que nous puissions nous référer à notre bibliothèque de composants de la manière suivante.
import Vue from 'vue'
import vui from 'x-vui'
Vue . use ( vui ) Nous devons également emballer et gérer les fichiers pertinents dans examples , car nous devons développer le site officiel du document de la bibliothèque des composants plus tard, et les entrées pertinentes du site officiel du document sont dans examples
Nous pouvons voir dans le projet d'initialisation que webpack dans le fichier build est le suivant
├── webpack.base.conf.js 基础配置文件
├── webpack.dev.conf.js 本地服务配置文件
├── webpack.prod.conf.js 打包配置文件
├── webpack.test.conf.js 测试配置文件(这里先不做过多描述) Le répertoire de sortie de output initialisé est dist . Ce répertoire est le répertoire de sortie après l'emballage de l'ensemble du projet, pas le répertoire requis par notre bibliothèque de composants. Comme ce n'est pas ce que nous voulons, que voulons-nous faire dans le répertoire dont nous avons besoin?
lib/vui.js (Fichier principal de la bibliothèque de composants)lib/vui-css/index.css (le fichier principal de la bibliothèque de composants CSS, nous ne décrirons pas trop sur l'emballage CSS dans ce chapitre, et les chapitres suivants seront expliqués séparément)examples examples/dist du fichier d'exemples (l'entrée principale du site officiel du document ultérieur) Depuis que l'objectif a été fixé, la prochaine chose que nous devons faire est d'organiser d'abord les fichiers d'emballage webpack pertinents, comme suit
├── 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
Avant de commencer à transformer le fichier webpack.base.conf.js , nous devons comprendre ce qui doit être fait dans deux fichiers emballés.
webpack.build.js : sortit le fichier de composantes lib/vui.js js js, et utilisera webpack.base.conf.js et webpack.dev.conf.js Configurations connexeswebpack.build.min.js : publie examples/dist et utilise webpack.base.conf.js et webpack.dev.conf.js Configurations connexes Étant donné que les deux fichiers de packaging webpack utilisent webpack.base.conf.js et webpack.dev.conf.js des configurations connexes, pourquoi ne pas intégrer certains des mêmes fichiers dans les fichiers webpack.base.conf.js ? L'objectif est clair, suivons-moi ensuite
'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
Ici, il vous suffit de supprimer la configuration intégrée dans webpack.base.conf.js pour éviter la duplication du code
'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 )
}
} )
} ) Après que les deux fichiers de webpack.base.conf.js et webpack.dev.conf.js sont ajustés, exécutez à nouveau npm run dev .
L'image ci-dessus semble indiquer que votre fichier de service local a été modifié avec succès comme prévu ~
1. Webpack.build.js
L'objectif principal de ce fichier est d'emballer tous les fichiers liés aux composants dans la bibliothèque de composants ensemble et de sortir 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
L'objectif principal de ce fichier est d'ouvrir une seule adresse de package et de sortir les fichiers pertinents dans examples au répertoire des examples/dist (c'est-à-dire l'entrée officielle du site Web du document suivant)
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 Lorsque nous réunissons tous ces fichiers, la dernière étape consiste à écrire la commande package sur scripts de 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 "
}, Exécutez la commande, npm run build:vui , allez
À ce stade, le service local et deux fichiers emballés ont été transformés. Essayons d'utiliser npm ~
Notez que si vous n'avez pas votre propre compte npm , veuillez enregistrer un compte par vous-même sur le site officiel npm . Cliquez ici pour entrer le site officiel pour vous inscrire. Les étapes d'enregistrement sont relativement simples. Je ne le décrirai pas davantage ici. Si vous avez des questions, vous pouvez me poser dans le groupe 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) Alors confirmez, package.json sera généré comme suit
{
"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"
} Ensuite, nous devons nous connecter à notre compte npm enregistré localement
npm adduser
# Username: 填写你自己的npm账号
# Password: npm账号密码
# Email: (this IS public) 你npm账号的认证邮箱
# Logged in as xuqiang521 on https://registry.npmjs.org/. 连接成功Exécuter npm publish pour commencer à publier
npm publish
# + [email protected] Pour le moment npm vous pouvez rechercher et voir le package que vous venez de publier ~
À l'heure actuelle, nous avons écrit le composant hello le plus simple de la bibliothèque des composants, mais cela n'affecte pas du tout notre édition sur npm , et les étapes de publication sont aussi simples que l'exemple ci-dessus.
Modifiez certaines descriptions dans le fichier package.json
// npm 包js入口文件改为 lib/vui.js
"main" : "lib/vui.js" ,
// npm 发布出去的包包含的文件
"files" : [
"lib" ,
"src" ,
"packages"
] ,
// 将包的属性改为公共可发布的
"private" : false , Notez que lorsque le test du package npm est publié, n'oubliez pas que la version version dans package.json est plus élevée que la précédente.
Commencer à publier
# 打包,输出lib/vui.js
npm run build:vui
# 发布
npm publish
# + [email protected] Sélectionnez un projet VUE local et entrez le projet
npm i component-library-test
# or
cnpm i component-library-testEnregistrer les composants dans le fichier d'entrée du projet
import Vue from 'vue'
import vui from 'component-library-test'
Vue . use ( vui )Utiliser sur la page
< v-hello message =" component library " > </ v-hello > À ce stade, nous avons réussi à transformer les fichiers de service locaux, à implémenter le package du fichier principal de la bibliothèque des composants et le package de l'entrée principale du site Web officiel et nous avons finalement appris à utiliser npm pour la version du projet.
Dans le chapitre suivant, j'expliquerai l'emballage du fichier css dans la bibliothèque de composants.
Dans la section précédente, nous avons déjà emballé le fichier JS. Cependant, pour les bibliothèques de composants, ce que nous devons faire est non seulement de gérer le fichier JS, mais aussi de gérer le fichier CSS, afin de garantir l'utilisation ultérieure de la bibliothèque de composants.
Dans cette section, je vais expliquer comment utiliser raisonnablement gulp pour séparer l'emballage et la gestion des fichiers CSS dans des projets basés sur la construction webpack .
Avant de commencer, nous devons clarifier deux buts:
Afin de faciliter la gestion, chaque fois que nous créons un nouveau composant, nous devons créer un fichier CSS correspondant pour gérer le style du composant et atteindre une gestion unique.
Ici, nous stockons tous les fichiers CSS dans packages/vui-css . La structure spécifique est la suivante
├── src
│ ├── common 存放组件公用的css文件
│ ├── mixins 存放一些mixin的css文件
│ ├── index.css css主入口文件
│ ├── hello.css 对应hello组件的单一css文件
├── gulpfile.js css打包配置文件
├── package.json 相关的版本依赖Avant de commencer à écrire le composant CSS, nous devons clarifier certains points:
Je pense personnellement que la meilleure façon sur le marché est de gérer les composants dans un seul CSS et d'écrire le CSS à l'aide bem . Si vous voulez connaître bem , cliquez sur le lien ci-dessous
Ensuite, expliquons le simple composant hello . Avant de commencer, mettez le contenu de 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 > Créer hello.css dans packages/vui-css/src
@b v-hello {
color : # fff ;
transform : scale ( 1 );
@e message {
background : # 0067ED ;
}
} Puis importez le fichier hello.css dans l' index.css d'entrée principal.css
@import './hello.css' ; Introduction des styles de bibliothèque de composants dans examples/src/index.js
import 'packages/vui-css/src/index.css' Mais à partir du contenu hello.css , nous pouvons voir qu'il s'agit d'une méthode d'écriture bem typique et ne peut pas être analysé normalement. Nous devons introduire le plugin postcss correspondant pour analyser la syntaxe bem . Ici, nous utiliserons le plugin postcss-salad développé饿了么团队pour analyser la syntaxe bem . Deuxièmement, ce fichier CSS de style sass-like doit également utiliser un plugin appelé precss . Installez d'abord les dépendances.
npm i postcss-salad precss -D Une fois l'installation de dépendance terminée, nous devons créer salad.config.json dans le répertoire racine du projet pour configurer les règles bem . Les règles spécifiques sont les suivantes
{
"browsers" : [ " ie > 8 " , " last 2 versions " ],
"features" : {
"bem" : {
"shortcuts" : {
"component" : " b " ,
"modifier" : " m " ,
"descendent" : " e "
},
"separators" : {
"descendent" : " __ " ,
"modifier" : " -- "
}
}
}
} Ensuite, nous devons utiliser postcss-salad et les plugins precss dans le fichier .postcssrc initialisé, comme suit
module . exports = {
"plugins" : {
"postcss-import" : { } ,
"postcss-salad" : require ( './salad.config.json' ) ,
"postcss-url" : { } ,
"precss" : { } ,
"autoprefixer" : { } ,
}
}OK, lorsque vous exécutez à nouveau le projet pour le moment, vous verrez que le CSS prend effet, comme indiqué sur la figure
Afin de mieux gérer les fichiers CSS dans la bibliothèque de composants et de s'assurer que les utilisateurs peuvent également introduire les fichiers CSS correspondant au composant lorsque seul ou plusieurs composants de la bibliothèque de composants sont introduits. Par conséquent, nous devons emballer le fichier CSS séparément. Ici, nous devons utiliser gulp pour effectuer l'opération d'emballage correspondante. Avant de commencer à obtenir les détails de l'emballage, assurez-vous que vous avez installé gulp à l'échelle mondiale. Sinon, veuillez l'installer
npm i gulp -g
# 查看版本
gulp -v
# CLI version 3.9.1 Ensuite, jetons un coup d'œil à ce que les dépendances doivent être utilisées dans le fichier 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" : { }
} Nous pouvons voir que cela est en fait similaire à la dépendance requise pour les fichiers CSS dans la bibliothèque de composants, sauf qu'il s'agit du plug-in postcss basé sur gulp . Avant de commencer à configurer gulpfile.js , n'oubliez pas d'exécuter npm i pour l'installation de dépendance.
Ensuite, nous commençons à configurer gulpfile.js , comme suit
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' ] ) Vous pouvez maintenant commencer à exécuter la commande gulp build pour emballer le fichier CSS. Bien sûr, afin de faciliter et de mieux exécuter les commandes d'emballage, nous devons maintenant ajouter une commande CSS Build à package.json dans le répertoire racine du projet, comme suit
"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 "
} Exécuter npm run build:vui-css , allez-y et enfin emballé les fichiers JS et CSS de la bibliothèque de composants comme indiqué dans la figure suivante
OK, à ce stade, vous pouvez déjà introduire des composants et leurs styles séparément. Enfin, afin de permettre aux utilisateurs d'utiliser directement le CSS de votre composant, n'oubliez pas de le publier sur npm ~ les étapes sont les suivantes
# 进到vui-css目录
cd packages/vui-css
# 发布
npm publishÀ ce stade, nous avons terminé la gestion et l'emballage séparé du fichier CSS et terminé la sortie unique du fichier CSS. De cette façon, nous pouvons avoir une meilleure façon de développer et de gérer le fichier CSS de la bibliothèque de composants tout en étant en mesure de faciliter l'utilisation de la bibliothèque de composants!
Jusqu'à présent, nous avons construit un nouveau répertoire requis pour la bibliothèque de composants, et nous avons également transformé l'emballage des fichiers JS et des fichiers CSS. Nous avons fait suffisamment de préparations pour le développement de la bibliothèque des composants, mais nous devons toujours faire un pré-travail très important pour faciliter le développement et le maintien des composants suivants de la bibliothèque de composants.
Pour les tests frontaux, il s'agit d'une branche importante de l'ingénierie frontale. Par conséquent, comment manquer une partie aussi importante dans notre bibliothèque de composants? Pour les tests unitaires, il existe principalement deux types
Dans ce chapitre, je vous Karma à des composants de test unitaires dans notre bibliothèque de composants en utilisant les deux frameworks basés sur l'initialisation Mocha .
Je crois que la plupart des gens qui ont été exposés aux tests unitaires connaissent les deux cadres, Karma + Mocha , mais ici je pense qu'il est nécessaire d'ouvrir une section distincte pour donner une brève introduction aux deux cadres.
Afin de permettre aux composants de notre bibliothèque de composants de s'exécuter dans les principaux navigateurs Web grand public pour les tests, nous avons choisi le karma . La chose la plus importante est que le karma est un cadre de test unitaire recommandé par vue-cli . Si vous voulez en savoir plus sur Karma , veuillez consulter le site officiel de Karma
simple , flexible et funPromisecoverage du codebefore() , after() , beforeEach() et afterEach() , afin que nous puissions définir différentes opérations à différentes étapes pour mieux terminer nos tests. Ici, je présenterai trois usages de base de mocha , ainsi que les quatre fonctions Hook de describe (cycle de vie)
décrire (modulename, fonction): describe est niditable, décrivant si le cas de test est correct.
describe ( '测试模块的描述' , ( ) => {
// ....
} ) ; ** It (info, fonction): ** celui it correspond à un cas de test unitaire
it ( '单元测试用例的描述' , ( ) => {
// ....
} )Utilisation de la bibliothèque d'assertion
expect ( 1 + 1 ) . to . be . equal ( 2 ) Cycle de vie de describe
describe ( 'Test Hooks' , function ( ) {
before ( function ( ) {
// 在本区块的所有测试用例之前执行
} ) ;
after ( function ( ) {
// 在本区块的所有测试用例之后执行
} ) ;
beforeEach ( function ( ) {
// 在本区块的每个测试用例之前执行
} ) ;
afterEach ( function ( ) {
// 在本区块的每个测试用例之后执行
} ) ;
// test cases
} ) ; Les étudiants qui veulent en savoir plus sur les opérations mocha peuvent cliquer sur le lien ci-dessous pour le voir
Dans la section ci-dessus, je présente brièvement les cadres de test Karma et Mocha , qui sont officiellement recommandés par Vue. J'espère également que lorsque vous voyez cela, vous pouvez avoir une compréhension simple des tests unitaires et des frameworks de test communs.
Avant le début du test unitaire réel, jetons un coup d'œil à la configuration du karma . Ici, nous regardons directement la configuration du fichier karma.conf.js initialisé par l'échafaud vue-cli (j'ai commenté l'utilisation spécifique)
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' }
]
}
} )
} Ensuite, effectuons un test simple sur notre propre composant hello (n'écrivons qu'un seul cas de test), créez un nouveau fichier hello.spec.js dans test/unit/specs et écrivez le code suivant
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
} )
} ) Après avoir écrit l'exemple de test, l'étape suivante consiste à effectuer le test. Exécuter npm run test , allez vers vous ~, sortie le résultat
hello.vue
✓ render default classList in hello D'après l'instance de test ci-dessus du composant hello , nous devons instancier le composant dans une instance Vue, et parfois nous devons le monter sur le DOM
const Constructor = Vue . extend ( Hello )
const vm = new Constructor ( {
propsData : {
message : 'component'
}
} ) . $mount ( ) Si chaque composant a plusieurs instances de test unitaire plus tard, cette écriture entraînera une ballonnement de notre test final. Ici, nous pouvons nous référer à l'outil de test unitaire util.js encapsulé par element . Nous devons encapsuler certaines méthodes couramment utilisées dans les tests unitaires. Ci-dessous, je vais énumérer certaines méthodes fournies dans l'outil.
/**
* 回收 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 ) { } Ci-dessous, nous utiliserons la méthode de l'outil de test défini pour transformer l'instance de test du composant hello et transformer le fichier 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
} )
} ) Réexécuter npm run test et la sortie du résultat
hello.vue
✓ render default classList in hello Ci-dessus, nous avons introduit l'utilisation des tests unitaires sur les jugements statiques. Ensuite, nous testerons certains cas d'utilisation asynchrones et certains événements interactifs. Avant les tests, nous devons modifier légèrement le code de notre composant hello , comme suit
< 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 > Ensuite, nous voulons tester si hello peut emit avec succès des informations par promesse. Par exemple, le cas de test est le suivant
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 )
} ) Recommencer le test, exécuter npm run test , sortir le résultat
hello.vue
✓ render default classList in hello
✓ create a hello for click with promiseÀ ce stade, nous avons appris la configuration des tests unitaires et certaines utilisations couramment utilisées. Si vous avez besoin d'en savoir plus sur les tests unitaires, veuillez suivre le lien que j'ai fourni plus tôt pour accéder à une étude plus profonde
Amis, suivez-moi pour pratiquer les 5 chapitres précédents et a construit l'étagère de base pour notre développement de composants. Ensuite, je vous emmènerai tous pour terminer le site officiel du document avec des composants importants importants dans la bibliothèque des composants.
Tout le monde doit savoir que les bons projets open source doivent avoir des sites Web de documentation officiels, donc pour faire de notre bibliothèque d'interface utilisateur l'un des meilleurs, nous devons également utiliser notre propre site Web officiel de documentation.
Un bon site Web officiel de documents nécessite deux choses.
Étant donné que la bibliothèque de composants que je vous ai conduit à développer convient aux appareils mobiles, comment pouvons-nous faire que notre site Web officiel de document a à la fois des descriptions de documentation API et des démos d'exemples mobiles. Cela nous oblige à développer deux ensembles de pages d'adaptation. Les choses suivantes que nous devons faire:
Avant le début du combat réel, jetons un coup d'œil à la structure du répertoire nécessaire dans ce chapitre.
├── 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 动态注册路由Ce chapitre vous emmène principalement pour réaliser la conversion des fichiers Markdown et l'adaptation de routage de différents appareils.
Après avoir clarifié les idées, continuons à développer notre site Web officiel de documents!
À partir du répertoire que j'ai donné ci-dessus, nous pouvons voir que le dossier DOCS est stocké dans le fichier Markdown, et chaque fichier Markdown correspond au document API d'un composant. Le résultat que nous voulons est de convertir chaque fichier Markdown dans les documents pour le transformer en composants Vue et d'enregistrer les composants Vue convertis en route afin qu'il puisse accéder à chaque fichier de marque via l'itinéraire.
Pour l'analyse des fichiers Markdown dans les composants VUE, il existe de nombreux plug-ins webpack tridits sur le marché. Bien sûr, si vous avez une compréhension approfondie de webpack , vous pouvez également essayer d'en choisir un seul. Ici, j'utilise directement Vue-Markdown-chargedeur développé par饿了么团队
La première étape consiste à compter sur l'installation
npm i vue-markdown-loader -D La deuxième étape consiste à utiliser vue-markdown-loader dans le fichier webpack.base.conf.js
{
test : / .md$ / ,
loader : 'vue-markdown-loader' ,
options : {
// 阻止提取脚本和样式标签
preventExtract : true
}
} La troisième étape est d'essayer. Ajoutez d'abord le fichier hello.md dans docs , puis écrivez les instructions pour utiliser hello Components
## 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 | 点击操作 | — | Étape 4: Enregistrez hello.md dans l'itinéraire
route . push ( {
path : '/component/hello' ,
component : require ( '../docs/hello.md' )
} ) Enfin, visitez la page. À hello.md heure
Bien sûr, je n'ai pas besoin d'expliquer cette situation, vous le savez peut-être aussi. Oui, le fichier de démarrage analysé est si laid, simplement parce que nous n'avons ni mis en évidence le thème de notre fichier de démarrage ni défini le style de base de la page de document. Ainsi, ensuite, nous devons ajouter un joli thème mis en évidence et un style de base propre à notre fichier de démarrage.
Pour les sujets, nous utiliserons ici le thème Atom-One-Dark dans highlight.js .
La première étape consiste à installer highlight.js
npm i highlight -D La deuxième étape consiste à introduire le thème dans examples/src/App.vue , et afin de définir le style de base du document, nous devons également modifier la mise en page d'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 > La troisième étape consiste à définir le style de base du document. Créez de nouveaux docs.css dans assets et écrivez le style initial. Étant donné que le volume de code est trop grand, je ne le publierai pas ici. Vous pouvez copier le code dans docs.css dans le fichier docs.css local par vous-même, puis l'importer dans examples/src/index.js
import '../assets/docs.css' Enfin, transformez les règles d'analyse Markdown. vue-markdown-loader fournit une interface preprocess pour que nous puissions fonctionner librement. Ensuite, nous définissons la structure du fichier de marquage analysé et l'écrivons dans le fichier 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 ;
}
}
}Ensuite, revisitez LocalHost: 8080 / # / Composant / Hello
Ok, notre fichier MD a été analysé avec succès dans les composants Vue, et a un beau thème mis en évidence et un style de base simple ~
Comme je l'ai déjà dit, la bibliothèque de composants développée par cet article est adaptée au mobile, nous devons donc afficher des documents sur le PC et les démos sur le mobile.
Dans cette section, je vous emmènerai pour adapter les itinéraires à différentes extrémités. Bien sûr, cette chose n'est pas difficile. Il utilise principalement WebPack pour créer des fonctionnalités de plusieurs pages. Alors, comment le faire spécifiquement? Ok, commençons maintenant
La première étape consiste à enregistrer le fichier d'entrée JS et à l'écrire dans le fichier webpack.base.conf.js
entry: {
// ...
'vui' : './examples/src/index.js' , // PC端入口js
'vui-mobile' : './examples/src/mobile.js' // 移动端入口js
} La deuxième étape consiste à enregistrer l'entrée de la page et à l'écrire dans le fichier 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
} )
] L'enregistrement du fichier d'entrée est terminé et ce que nous devons faire ensuite, c'est déterminer l'environnement de l'appareil. Ici, j'utiliserai navigator.userAgent avec des expressions régulières pour déterminer si l'environnement dans lequel l'exécution de notre bibliothèque de composants appartient au côté PC ou au côté mobile?
La première étape consiste à rédiger le code suivant dans 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 La deuxième étape consiste à rédiger les règles de jugement suivant dans les examples/src/index.js du côté PC
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 ( )
} ) La troisième étape consiste à rédiger des règles de jugement similaires à l'étape précédente des 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 ( )
} ) Enfin, améliorez examples/src/mobile.js et le fichier MobileApp.vue portail mobile
Écrivez le code suivant dans 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/>'
} ) Écrivez dans MobileApp.vue
< template >
< div class =" mobile-container " >
< router-view > </ router-view >
</ div >
</ template >Ensuite, vous pouvez essayer l'effet dans le navigateur pour voir si différents environnements de périphérique peuvent afficher le contenu correspondant ~
À ce stade, tous les plans que nous avons formulés dans ce chapitre ont été achevés. Conversion "parfaite" des fichiers MD et adaptation du routage dans différents environnements d'appareils. Le développement du site officiel du document (partie 1) touche à sa fin ici. Dans le chapitre suivant, nous continuerons à terminer les travaux de développement restants du site officiel du document!
Dans le chapitre précédent, nous avons terminé:
Dans ce chapitre, nous améliorerons les détails du site officiel du document et développerons un site officiel complet du document.
Dans le répertoire donné dans le chapitre précédent, nous pouvons savoir que le répertoire DOCS est utilisé pour stocker des fichiers MD que le PC doit afficher et que le répertoire des pages est utilisé pour stocker des fichiers de démonstration mobiles. Alors, comment les composants peuvent-ils afficher leurs fichiers correspondants dans différents environnements de périphérique (le côté PC affiche les fichiers MD correspondant aux composants, et le côté mobile affiche les fichiers Vue correspondant aux composants)? Comment pouvons-nous raisonnablement gérer le routage de notre bibliothèque de composants dans ce cas? Ensuite, nous continuons le développement ci-dessous en fonction de ces questions. is-mobile.js sera certainement utilisé ici pour déterminer l'environnement de l'appareil. Veuillez me suivre pour faire le travail spécifique.
La première étape consiste à créer un nouveau fichier nav.config.json Fichier sous examples/src et à écrire le contenu suivant
{
// 为了之后组件文档多语言化
"zh-CN" : [
{
"name" : "Vui 组件" ,
"showInMobile" : true ,
"groups" : [
{
// 管理相同类型下的所有组件
"groupName" : "基础组件" ,
"list" : [
{
// 访问组件的相对路径
"path" : "/hello" ,
// 组件描述
"title" : "Hello"
}
]
}
]
}
]
} La deuxième étape consiste à améliorer le fichier router.config.js et le transformer en une fonction d'assistance pour l'enregistrement de routage.
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 La troisième étape consiste à enregistrer l'itinéraire dans le portail principal JS examples/src/index.js des examples/src/mobile.js du code principal JS Exemples / src / mobile.js du côté mobile, et écrivez le code suivant JS
import registerRoute from './router.config'
import navConfig from './nav.config'
const routesConfig = registerRoute ( navConfig )
const router = new VueRouter ( {
routes : routesConfig
} )Puis visitez notre site officiel de document de bibliothèque de composants actuel
D'après les rendus finaux du chapitre précédent, nous pouvons voir que le terminal PC est divisé en trois parties, à savoir:
Ensuite, commençons à afficher l'API PC
L'en-tête est relativement simple. Nous devons seulement créer un nouveau fichier page-header.vue sous examples/src/components et écrire le contenu suivant
< 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 >Pour des styles spécifiques, veuillez visiter Page-Header.Vue directement pour les visualiser.
Sur le côté gauche, nous montrons les itinéraires et les titres des composants. En fait, il s'agit d'analyser et d'afficher examples/src/nav.config.json .
Nous créons un nouveau fichier side-nav.vue sous examples/src/components . La structure normale du fichier est la suivante
< 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 > Mais nous devons maintenant analyser examples/src/nav.config.json en fonction de la structure actuelle. Le code amélioré est le suivant
< 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 >Cliquez ici pour le code complet Side-Nav.vue
Nous utilisons page-header.vue et side-nav.vue que nous avons écrit dans 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 >Ensuite, visitez à nouveau la page, le résultat est comme indiqué sur l'image
Les principes de la démo mobile et du PC sont similaires. Les deux doivent analyser le fichier nav.config.json pour l'affichage
À l'heure actuelle, à l'exception de la page d'entrée principale MobileApp.vue , notre terminal mobile n'a pas de dépendance des composants racine. Ensuite, nous terminerons d'abord le développement du composant racine, créerons un nouveau fichier demo-list.vue sous examples/src/components et écrire du contenu
< template >
< div class =" side-nav " >
< h1 class =" vui-title " > </ h1 >
< h2 class =" vui-desc " > VUI 移动组件库</ h2 >
</ div >
</ template > Ensuite, nous devons le référencer dans l'itinéraire et l'écrire dans le fichier mobile.js
import DemoList from './components/demo-list.vue'
routesConfig . push ( {
path : '/' ,
component : DemoList
} ) Ensuite, commencez à améliorer 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
美团点评长期招人,如果有兴趣的话,欢迎一起搞基,简历投递方式交流群中有说明~
Amis, qu'attendez-vous? Hurry up and like the article first, then follow me, and then join the group to communicate with the big guys~~~