나는 보통 일부 구성 요소를 포함하여 몇 가지 사항을 기록하고 요약하고 싶습니다. 많은 것을 축적 할 때, 나는 흩어진 축적이 더 이상 관리에 적합하지 않다는 것을 알았습니다.
그래서 생각하기 시작했습니다. 비교적 표준화 된 방식으로 더 흩어진 것들을 관리하는 좋은 방법이 있습니까? 구성 요소가 구성 요소 라이브러리 형태로 관리되는 경우 자신의 축적에 더 적합하고 향후 작업을 용이하게합니까?
그래서 나는 element-ui , vux , vant 등과 같은 시장에있는 우수한 UI 구성 요소 라이브러리를 언급하고, 아키텍처의 구성을 이해하기 위해 소스 코드를 읽은 다음 내 자신의 모바일 UI 구성 요소 라이브러리 vui 세트를 정렬하기 시작했습니다.
여가 시간에는 주요 기술 커뮤니티에서 활동하고 있습니다. 나는 종종 한동안 일한 친구들이 있거나 저자에게 몇 가지 질문을하기 위해 인턴쉽을 찾을 준비를하고있는 친구들이 있습니다. 구성 요소 라이브러리를 만드는 방법? 구성 요소 라이브러리를 직접 만드는 것이 이력서의 하이라이트가됩니까? 구성 요소 라이브러리 개발에 관한 기사를 쓸 수 있습니까? ...
이 블로그 게시물은 의심에 응답하고 질문을 공유하는 분위기에서 태어났습니다.
기사를 읽을 때 궁금한 점이 있으면 토론 그룹에 가입하여 토론하십시오 (매일 이야기하는 큰 남자 그룹 외에도 소녀 그룹도 있습니다 ~)
프론트 엔드 Hodgepodge : 731175396
github : https://github.com/xuqiang521
더 이상 고민하지 않고 실제 전투 장으로 직접 가자 ~
여기서는 Mac과 창 아래에 노드 설치에 대해서만 이야기하겠습니다.
Mac Package Manager homebrew 설치하지 않은 경우 첫 번째 단계는 먼저 설치하는 것입니다.
/usr/bin/ruby -e " $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install ) " homebrew 사용하여 node 설치하십시오
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 의 init 지침을 사용하여 personal-components-library 라는 프로젝트 초기화
# 项目基于 webpack
vue init webpack personal-components-library구성 할 때 스캐 폴딩은 프로젝트의 설명과 종속성을 작성하도록 요청합니다. 아래에서 선택한 내용을 참조하여 작성하십시오.
# 项目名称
Project name ? personal-components-library
# 项目描述
Project description ? A Personal Vue.js components Library project
# 项目作者
Author ? qiangdada
# 项目构建 vue 版本(选择默认项)
Vue build ? standalone
# 是否下载 vue-router (后期会用到,这里选 Yes)
Install vue-router ? Yes
# 是否下载 eslint (为了制定合理的开发规范,这个必填)
Use ESLint to lint your code ? Yes
# 安装默认的标准 eslint 规则
Pick an ESLint preset ? Standard
# 构建测试案例
Set up unit tests ? Yes
# 安装 test 依赖 (选择 karma + mocha)
Pick a test runner ? karma
# 构建 e2e 测试案例 (No)
Setup e2e tests with Nightwatch ? No
# 项目初始化完是否安装依赖 (npm)
Should we run ` npm install ` for you after the project has been created ? (recom
mended) npm 선택한 후에는 기다릴 수 있습니다. vue-cli 프로젝트를 구축하고 종속성 설치를 수행하는 데 도움이됩니다.
초기화 프로젝트 구조는 다음과 같습니다.
├── build webpack打包以及本地服务的文件都在里面
├── config 不同环境的配置都在这里
├── index.html 入口html
├── node_modules npm安装的依赖包都在这里面
├── package.json 项目配置信息
├── README.md 项目介绍
├── src 我们的源代码
│ ├── App.vue vue主入口文件
│ ├── assets 资源存放(如图片)
│ ├── components 可以复用的模块放在这里面
│ ├── main.js 入口js
│ ├── router 路由管理
└── webpack.config.js webpack配置文件
├── static 被copy的静态资源存放地址
├── test 测试文档和案例npm 사용하여 종속성을 너무 느리게 다운로드하거나 일부 리소스가 벽으로 표시되면 cnpm 사용하여 종속성을 다운로드하는 것이 좋습니다.
# 全局安装 cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 使用 cnpm 进行依赖安装
cnpm i 종속성 설치가 완료된 후 vue 프로젝트를 시작할 수 있습니다 ~
npm run dev 그런 다음 http://localhost:8080 방문하면 vue-cli 통해 구축 된 vue 프로젝트에 성공적으로 액세스 할 수 있습니다. 이 시점에서 구성 요소 라이브러리에 의존하는 개발 환경이 설치되었습니다.
우선, 우리는이 섹션의 목적을 명확히해야합니다. 구성 요소 라이브러리를 더 잘 개발하려면 디렉토리를 수정해야합니다.
우리는 이미 이전 섹션에서 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 组件注册主入口 이것에 대해 생각해 보면, 당신이 이것을 볼 때, 당신은 또한 우리가 지금해야 할 일을 알아야합니다. 맞습니다. 로컬 서비스의 입력 파일을 수정하는 것입니다. 실행할 수있는 경우 entry 의 JS 항목 및 html-webpack-plugin 의 페이지 항목 참조를 수정하십시오. 코드는 다음과 같습니다 (주요 코드 만 배치)
entry: {
'vendor' : [ 'vue' , 'vue-router' ] ,
'vui' : './examples/src/index.js'
} ,
// ...
plugins : [
// ...
// 将入口改成examples/src/index.tpl
new HtmlWebpackPlugin ( {
chunks : [ 'vendor' , 'vui' ] ,
template : 'examples/src/index.tpl' ,
filename : 'index.html' ,
inject : true
} )
] 좋아, 수정. npm run dev 다시 실행하면 프로젝트가 새 입력 파일에서 실행될 수 있습니다.
이 섹션에서 구현해야 할 것은 로컬로 시작한 서비스이며 packages 아래의 구성 요소를 사용할 수 있습니다. 설명하기 위해 가장 간단한 hello 구성 요소를 개발합시다
packages 아래에서 hello 구성 요소를 만듭니다 구속력이 좋은 특성을 갖기 위해서는 여기에서 우리는 다음을 제한합니다. 구성 요소 작성을 시작하기 전에 지정된 디렉토리와 파일 이름을 균일하게 관리해야합니다. packages 디렉토리의 hello 구성 요소 아래의 파일은 다음과 같습니다.
├── hello
│ ├── hello.vue hello.vue 컨텐츠는 다음과 같습니다
< template >
< div class =" v-hello " >
hello {{ message }}
</ div >
</ template >
< script >
export default {
name : 'v-hello' ,
props : {
message : String
}
}
</ script > 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 entry js 파일의 참조 다음으로, 이전 섹션의 수정 된 examples 에서 쓴 hello 구성 요소를 참조해야합니다.
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
초기화 프로젝트에서 build 파일의 webpack 파일이 다음과 같습니다.
├── 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.js 관련 구성을 사용합니다.webpack.build.min.js : examples/dist 문서 관련 파일을 출력하고 webpack.base.conf.js 및 webpack.dev.conf.js 관련 구성을 사용합니다. webpack Packaging 파일 모두 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 디렉토리 (즉, 후속 문서의 공식 웹 사이트 입력)에 출력하는 것입니다.
const path = require ( 'path' )
const webpack = require ( 'webpack' )
const merge = require ( 'webpack-merge' )
const baseWebpackConfig = require ( './webpack.base.conf' )
const config = require ( '../config' )
const ExtractTextPlugin = require ( 'extract-text-webpack-plugin' )
const webpackConfig = merge ( baseWebpackConfig , {
output : {
chunkFilename : '[id].[hash].js' ,
filename : '[name].min.[hash].js'
} ,
plugins : [
new webpack . optimize . UglifyJsPlugin ( {
compress : {
warnings : false
} ,
output : {
comments : false
} ,
sourceMap : false
} ) ,
// extract css into its own file
new ExtractTextPlugin ( {
filename : '[name].[contenthash].css' ,
allChunks : true ,
} ) ,
// keep module.id stable when vendor modules does not change
new webpack . HashedModuleIdsPlugin ( ) ,
// enable scope hoisting
new webpack . optimize . ModuleConcatenationPlugin ( ) ,
// split vendor js into its own file
new webpack . optimize . CommonsChunkPlugin ( {
name : 'vendor' ,
minChunks ( module ) {
// any required modules inside node_modules are extracted to vendor
return (
module . resource &&
/ .js$ / . test ( module . resource ) &&
module . resource . indexOf (
path . join ( __dirname , '../node_modules' )
) === 0
)
}
} ) ,
new webpack . optimize . CommonsChunkPlugin ( {
name : 'manifest' ,
minChunks : Infinity
} ) ,
new webpack . optimize . CommonsChunkPlugin ( {
name : 'app' ,
async : 'vendor-async' ,
children : true ,
minChunks : 3
} ) ,
]
} )
module . exports = webpackConfig 이 모든 파일을 함께 가져 오면 마지막 단계는 package.json 의 scripts 에 패키지 명령을 작성하는 것입니다.
"scripts" : {
"build:vui" : " webpack --progress --hide-modules --config build/webpack.build.js && rimraf examples/dist && cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.build.min.js "
}, 명령, npm run build:vui , 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 패키지를 테스트 할 때는 package.json 의 version 이 이전 버전보다 높다는 것을 기억하십시오.
출판을 시작하십시오
# 打包,输出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 파일을 관리하여 구성 요소 라이브러리의 후속 사용을 보장하는 것입니다.
이 섹션에서는 webpack Construction을 기반으로 프로젝트에서 CSS 파일의 포장 및 관리를 별도로 사용하여 gulp 합리적으로 사용하는 방법에 대해 이야기합니다.
시작하기 전에 두 가지 목표를 명확히해야합니다.
관리를 용이하게하기 위해 새 구성 요소를 만들 때마다 구성 요소의 스타일을 관리하고 단일 관리를 달성하기 위해 해당 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의 구성 요소를 관리하고 bem 사용하여 CSS를 작성하는 것이라고 생각합니다. 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 > packages/vui-css/src 디렉토리에서 hello.css 만듭니다
@b v-hello {
color : # fff ;
transform : scale ( 1 );
@e message {
background : # 0067ED ;
}
} 그런 다음 주요 입구 index.css 에서 hello.css 파일을 가져옵니다.
@import './hello.css' ; examples/src/index.js 에서 구성 요소 라이브러리 스타일 소개
import 'packages/vui-css/src/index.css' 그러나 hello.css 컨텐츠에서, 우리는 이것이 전형적인 bem 쓰기 방법이며 정상적으로 구문 분석 할 수 없음을 알 수 있습니다. bem 구문을 구문 분석하려면 해당 postcss 플러그인을 소개해야합니다. 여기서는饿了么团队개발 한 postcss-salad 플러그인을 사용하여 bem 구문을 구문 분석합니다. 둘째,이 sass-like 스타일 CSS 파일은 precss 라는 플러그인을 사용해야합니다. 의존성을 먼저 설치하십시오.
npm i postcss-salad precss -D 종속성 설치가 완료되면 bem 규칙을 구성하려면 Project Root 디렉토리에서 salad.config.json 만들어야합니다. 특정 규칙은 다음과 같습니다
{
"browsers" : [ " ie > 8 " , " last 2 versions " ],
"features" : {
"bem" : {
"shortcuts" : {
"component" : " b " ,
"modifier" : " m " ,
"descendent" : " e "
},
"separators" : {
"descendent" : " __ " ,
"modifier" : " -- "
}
}
}
} 다음으로 초기화 된 .postcssrc 파일의 postcss-salad 및 precss 플러그인을 사용해야합니다.
module . exports = {
"plugins" : {
"postcss-import" : { } ,
"postcss-salad" : require ( './salad.config.json' ) ,
"postcss-url" : { } ,
"precss" : { } ,
"autoprefixer" : { } ,
}
}자, 현재 프로젝트를 다시 실행하면 그림과 같이 CSS가 적용되는 것을 알 수 있습니다.
구성 요소 라이브러리에서 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" : { }
} 이것이 gulp 기반으로 한 postcss 플러그인임을 제외하고는 구성 요소 라이브러리의 CSS 파일에 필요한 종속성과 실제로 유사하다는 것을 알 수 있습니다. 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' ] ) 이제 CSS 파일을 패키지하기 위해 gulp build 명령을 실행할 수 있습니다. 물론, 포장 명령을 용이하게하고 더 잘 실행하려면 이제 프로젝트의 루트 디렉토리에서 CSS 빌드 명령을 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 파일의 포장도 변환했습니다. 구성 요소 라이브러리 개발을 위해 충분한 준비를했지만 구성 요소 라이브러리의 후속 구성 요소의 개발 및 유지 보수를 용이하게하기 위해 여전히 매우 중요한 사전 작업을해야합니다.
프론트 엔드 테스트의 경우 프론트 엔드 엔지니어링의 중요한 지점입니다. 따라서 구성 요소 라이브러리에서 어떻게 그러한 중요한 부분을 놓칠 수 있습니까? 단위 테스트의 경우 주로 두 가지 유형이 있습니다
이 장에서는 Mocha 초기화를 기반으로 두 프레임 워크를 사용하여 구성 요소 라이브러리의 단위 테스트 구성 요소로 연결 Karma .
단위 테스트에 노출 된 대부분의 사람들은 두 프레임 워크 인 Karma + Mocha 에 익숙하다고 생각하지만 여기서는 두 프레임 워크에 대한 간단한 소개를 제공하기 위해 별도의 섹션을 열어야한다고 생각합니다.
컴포넌트 라이브러리의 구성 요소가 테스트를 위해 주요 주류 웹 브라우저에서 실행할 수 있도록 Karma를 선택했습니다. 가장 중요한 것은 Karma 가 vue-cli 가 권장하는 단위 테스트 프레임 워크라는 것입니다. Karma 에 대해 더 알고 싶다면 Karma의 공식 웹 사이트를 확인하십시오.
simple 하고 flexible fun 테스트 프레임 워크입니다.Promise 과 같은 비동기 이미지 테스트 사용 사례를 지원합니다.coverage 테스트 보고서를 지원합니다before() , beforeEach() after() afterEach() 및 af 여기에서는 mocha 의 세 가지 기본 사용법과 describe 의 4 가지 후크 기능 (수명주기)을 소개합니다.
설명 (moduleName, function) : 테스트 사례가 올바른지 설명하는 describe 중첩 될 수 있습니다.
describe ( '测试模块的描述' , ( ) => {
// ....
} ) ; ** IT (정보, 기능) : ** 단위 테스트 케이스에 해당 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 운영에 대해 더 알고 싶은 학생들은 아래 링크를 클릭하여이를 볼 수 있습니다.
위의 섹션에서는 VUE가 공식적으로 권장하는 테스트 프레임 워크 Karma 와 Mocha 간단히 소개합니다. 또한 이것을 볼 때 단위 테스트 및 일반적인 테스트 프레임 워크에 대한 간단한 이해를 가질 수 있기를 바랍니다.
실제 단위 테스트가 시작되기 전에 카르마 의 구성을 살펴 보겠습니다. 여기서 우리는 vue-cli 스캐 폴드에 의해 초기화 된 karma.conf.js 파일의 구성을 직접 살펴 봅니다 (특정 사용에 대해 댓글을 달았습니다).
var webpackConfig = require ( '../../build/webpack.test.conf' )
module . exports = function karmaConfig ( config ) {
config . set ( {
// 浏览器
browsers : [ 'PhantomJS' ] ,
// 测试框架
frameworks : [ 'mocha' , 'sinon-chai' , 'phantomjs-shim' ] ,
// 测试报告
reporters : [ 'spec' , 'coverage' ] ,
// 测试入口文件
files : [ './index.js' ] ,
// 预处理器 karma-webpack
preprocessors : {
'./index.js' : [ 'webpack' , 'sourcemap' ]
} ,
// webpack配置
webpack : webpackConfig ,
// webpack中间件
webpackMiddleware : {
noInfo : true
} ,
// 测试覆盖率报告
coverageReporter : {
dir : './coverage' ,
reporters : [
{ type : 'lcov' , subdir : '.' } ,
{ type : 'text-summary' }
]
}
} )
} 다음으로, 우리 자신의 hello 구성 요소 (하나의 테스트 케이스 만 쓰기)에 대해 간단한 테스트를 수행하고 test/unit/specs 에서 새 hello.spec.js 파일을 작성하고 다음 코드를 작성합시다.
import Vue from 'vue' // 导入Vue用于生成Vue实例
import Hello from 'packages/hello' // 导入组件
// 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite)
describe ( 'Hello.vue' , ( ) => {
// 每个describe块应该包括一个或多个it块,称为测试用例(test case)
it ( 'render default classList in hello' , ( ) => {
const Constructor = Vue . extend ( Hello ) // 获得Hello组件实例
const vm = new Constructor ( ) . $mount ( ) // 将组件挂在到DOM上
// 断言:DOM中包含class为v-hello的元素
expect ( vm . $el . classList . contains ( 'v-hello' ) ) . to . be . true
const message = vm . $el . querySelector ( '.v-hello__message' )
// 断言:DOM中包含class为v-hello__message的元素
expect ( message . classList . contains ( 'v-hello__message' ) ) . to . be . true
} )
} ) 테스트 예제를 작성한 후 다음 단계는 테스트를 수행하는 것입니다. npm run test 실행, 이동 ~ ~, 결과를 출력하십시오.
hello.vue
✓ render default classList in hello hello 구성 요소의 위의 테스트 인스턴스에서 구성 요소를 VUE 인스턴스로 인스턴스화해야하며 때로는 DOM에 마운트해야합니다.
const Constructor = Vue . extend ( Hello )
const vm = new Constructor ( {
propsData : {
message : 'component'
}
} ) . $mount ( ) 각 구성 요소에 나중에 여러 개의 단위 테스트 인스턴스가 있으면이 쓰기는 최종 테스트가 부풀게됩니다. 여기서는 element 별로 캡슐화 된 단위 테스트 도구 util.js를 참조 할 수 있습니다. 단위 테스트에서 일반적으로 사용되는 일부 방법을 캡슐화해야합니다. 아래에는 도구에 제공된 몇 가지 방법을 나열합니다.
/**
* 回收 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 장을 연습하고 구성 요소 개발을위한 기본 선반을 만들었습니다. 다음으로, 구성 요소 라이브러리에서 중요한 구성 요소가있는 문서의 공식 웹 사이트를 완료하도록합니다.
모든 사람은 좋은 오픈 소스 프로젝트에 공식 문서 웹 사이트가 있어야한다는 것을 알아야하므로 UI 라이브러리를 최고 중 하나로 만들려면 자체 공식 문서 웹 사이트를 사용해야합니다.
좋은 공식 문서 웹 사이트에는 두 가지가 필요합니다.
내가 개발 한 구성 요소 라이브러리는 모바일 장치에 적합하기 때문에 공식 문서 웹 사이트에 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 动态注册路由이 장에서는 주로 Markdown 파일의 변환 및 다른 장치의 라우팅 적응을 실현할 수 있습니다.
아이디어를 명확히 한 후 공식 문서 웹 사이트를 계속 개발해 봅시다!
위에서 제시 한 디렉토리에서 Docs 폴더가 Markdown 파일에 저장되어 있으며 각 Markdown 파일은 구성 요소의 API 문서에 해당합니다. 우리가 원하는 결과는 문서의 모든 Markdown 파일을 변환하여 VUE 구성 요소로 전환하고 경로를 통해 각 Markdown 파일에 액세스 할 수 있도록 변환 된 VUE 구성 요소를 경로에 등록하는 것입니다.
마크 다운 파일을 VUE 구성 요소로 구문 분석하려면 시장에 많은 3 자 webpack 플러그인이 있습니다. 물론, webpack 을 깊이 이해하고 있다면 직접 선택할 수도 있습니다. 여기에서는饿了么团队개발 한 vue-markdown-loader를 직접 사용하고 있습니다.
첫 번째 단계는 설치에 의존하는 것입니다
npm i vue-markdown-loader -D 두 번째 단계는 webpack.base.conf.js 파일에서 vue-markdown-loader 사용하는 것입니다.
{
test : / .md$ / ,
loader : 'vue-markdown-loader' ,
options : {
// 阻止提取脚本和样式标签
preventExtract : true
}
} 세 번째 단계는 시도하는 것입니다. 먼저 docs 에 hello.md 파일을 추가 한 다음 hello 구성 요소 사용 지침을 작성하십시오.
## Hello
** Hello 组件,Hello 组件,Hello 组件,Hello 组件**
### 基本用法
```html
< template >
< div class = " hello-page " >
<v-hello message="my component library" @click="handleClick"></v-hello>
<p>{{ msg }}</p>
</ div >
</ template >
< script >
export default {
name : ' hello ' ,
data () {
return {
msg : ' '
}
},
methods : {
handleClick ( msg ) {
this . msg = msg
}
}
}
</ script >
```
### Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| ---------- | -------- | ---------- | ------------- | -------- |
| message | 文本信息 | string | — | — |
### Events
| 事件名称 | 说明 | 回调参数 |
| ---------- | -------- | ---------- |
| click | 点击操作 | — | 4 단계 : hello.md 경로에 등록하십시오
route . push ( {
path : '/component/hello' ,
component : require ( '../docs/hello.md' )
} ) 마지막으로 페이지를 방문하십시오. 현재 hello.md 의 내용이 VUE 구성 요소로 변환되어 라우팅로드를 통해 액세스 할 수 있지만 페이지는 추악합니다.
물론, 나는이 상황을 설명 할 필요가 없습니다. 당신도 그것을 알고있을 것입니다. 예, Parsed Markdown 파일은 마크 다운 파일의 테마를 강조하지 않았거나 문서 페이지의 기본 스타일을 설정하지 않았기 때문에 너무 추악합니다. 다음으로 Markdown 파일에 멋진 강조 표시된 테마와 깨끗한 기본 스타일을 추가해야합니다.
주제의 경우 여기에서는 highlight.js 의 Atom-One-Dark 테마를 사용합니다.
첫 번째 단계는 highlight.js 설치하는 것입니다
npm i highlight -D 두 번째 단계는 examples/src/App.vue 에 테마를 소개하는 것입니다. 문서의 기본 스타일을 설정하려면 App.Vue의 레이아웃도 수정해야합니다.
< template >
< div class =" app " >
< div class =" main-content " >
< div class =" page-container clearfix " >
< div class =" page-content " >
< router-view > </ router-view >
</ div >
</ div >
</ div >
</ div >
</ template >
< script >
import 'highlight.js/styles/atom-one-dark.css'
export default {
name : 'App'
}
</ script > 세 번째 단계는 문서의 기본 스타일을 설정하는 것입니다. assets 으로 새로운 docs.css 만들고 초기 스타일을 작성하십시오. 코드 볼륨이 너무 크기 때문에 여기에 게시하지 않습니다. docs.css의 코드를 직접 로컬 docs.css 파일에 복사 한 다음 examples/src/index.js
import '../assets/docs.css' 마지막으로 Markdown 구문 분석 규칙을 변환하십시오. 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 구성 요소에 성공적으로 구문 분석되었으며 아름다운 강조 표시된 테마와 간단한 기본 스타일을 가지고 있습니다.
앞에서 말했듯이,이 기사에서 개발 한 구성 요소 라이브러리는 모바일에 적용되므로 PC에 문서를 표시하고 모바일의 데모를 표시해야합니다.
이 섹션에서는 경로를 다른 목적으로 조정하도록합니다. 물론, 이것은 어렵지 않습니다. 주로 웹 팩을 사용하여 다중 페이지 기능을 구축합니다. 그렇다면 구체적으로 어떻게해야합니까? 좋아, 지금 시작하자
첫 번째 단계는 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 사용하여 정규 표현식을 사용하여 구성 요소 라이브러리가 실행되는 환경이 PC 쪽 또는 모바일 측면에 속하는지 여부를 결정합니다.
첫 번째 단계는 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 두 번째 단계는 JS 항목 파일 examples/src/index.js 에 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 ( )
} ) 세 번째 단계는 Mobile JS Entry File 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 부)의 개발이 여기에서 끝나고 있습니다. 다음 장에서는 문서 공식 웹 사이트의 나머지 개발 작업을 계속 완료 할 것입니다!
이전 장에서는 다음을 완료했습니다.
이 장에서는 문서 공식 웹 사이트의 세부 사항을 개선하고 전체 문서 공식 웹 사이트를 개발할 것입니다.
이전 장의 디렉토리에서 Docs 디렉토리는 PC에 표시 해야하는 MD 파일을 저장하는 데 사용되며 페이지 디렉토리는 모바일 데모 파일을 저장하는 데 사용됩니다. 그렇다면 구성 요소는 어떻게 다른 장치 환경에 해당 파일을 표시 할 수 있습니까 (PC 측은 구성 요소에 해당하는 MD 파일을 표시하고 모바일 측면은 구성 요소에 해당하는 VUE 파일을 표시합니까? 이 경우 구성 요소 라이브러리의 라우팅을 어떻게 합리적으로 관리 할 수 있습니까? 다음으로, 우리는 이러한 문제에 따라 아래에서 개발을 계속합니다. is-mobile.js 여기에서 장치 환경을 결정하는 데 분명히 사용됩니다. 특정 작업을 수행하려면 저를 따라 가십시오.
첫 번째 단계는 examples/src 에서 새 파일 nav.config.json 파일을 작성하고 다음 내용을 작성하는 것입니다.
{
// 为了之后组件文档多语言化
"zh-CN" : [
{
"name" : "Vui 组件" ,
"showInMobile" : true ,
"groups" : [
{
// 管理相同类型下的所有组件
"groupName" : "基础组件" ,
"list" : [
{
// 访问组件的相对路径
"path" : "/hello" ,
// 组件描述
"title" : "Hello"
}
]
}
]
}
]
} 두 번째 단계는 router.config.js 파일을 개선하고 라우팅 등록을 위해 도우미 기능으로 변경하는 것입니다.
const registerRoute = ( navConfig , isMobile ) => {
let route = [ ]
// 目前只有中文版的文档
let navs = navConfig [ 'zh-CN' ]
// 遍历路由文件,逐一进行路由注册
navs . forEach ( nav => {
if ( isMobile && ! nav . showInMobile ) {
return
}
if ( nav . groups ) {
nav . groups . forEach ( group => {
group . list . forEach ( nav => {
addRoute ( nav )
} )
} )
} else if ( nav . children ) {
nav . children . forEach ( nav => {
addRoute ( nav )
} )
} else {
addRoute ( nav )
}
} )
// 进行路由注册
function addRoute ( page ) {
// 不同的设备环境引入对应的路由文件
const component = isMobile
? require ( `../pages ${ page . path } .vue` )
: require ( `../docs ${ page . path } .md` )
route . push ( {
path : '/component' + page . path ,
component : component . default || component
} )
}
return route
}
export default registerRoute 세 번째 단계는 기본 포털 JS 파일의 기본 포털 JS 파일의 기본 포털 js 파일 예제 examples/src/index.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
} )그런 다음 현재 구성 요소 라이브러리 문서 공식 웹 사이트를 방문하십시오
이전 장의 최종 렌더링에서 PC 터미널이 세 부분으로 나뉘어 있음을 알 수 있습니다.
다음으로 PC API를 표시하기 시작하겠습니다
헤더는 비교적 간단합니다. examples/src/components 아래에서 새 page-header.vue 파일 만 만들고 다음 내용을 작성하면됩니다.
< template >
< div class =" page-header " >
< div class =" page-header__top " >
< h1 class =" page-header__logo " >
< a href =" # " > Vui.js </ a >
</ h1 >
< ul class =" page-header__navs " >
< li class =" page-header__item " >
< a href =" / " class =" page-header__link " >组件</ a >
</ li >
< li class =" page-header__item " >
< a href =" https://github.com/Brickies/vui " class =" page-header__github " target =" _blank " > </ a >
</ li >
< li class =" page-header__item " >
< span class =" page-header__link " > </ span >
</ li >
</ ul >
</ div >
</ div >
</ template >특정 스타일은 Page-Header.vue를 방문하여 직접 방문하십시오.
왼쪽에는 구성 요소 경로와 제목이 표시됩니다. 실제로, examples/src/nav.config.json 분석하고 표시하는 것입니다.
examples/src/components 아래에 새 side-nav.vue 파일을 만듭니다. 파일의 정상 구조는 다음과 같습니다
< li class =" nav-item " >
< a href =" javascript:void(0) " > Vui 组件</ a >
< div class =" nav-group " >
< div class =" nav-group__title " >基础组件</ div >
< ul class =" pure-menu-list " >
< li class =" nav-item " >
< router-link
active-class =" active "
:to =" /component/hello "
v-text =" navItem.title " > Hello
</ router-link >
</ li >
</ ul >
</ div >
</ li > 그러나 이제 현재 구조에 따라 examples/src/nav.config.json 구문 분석해야합니다. 개선 된 코드는 다음과 같습니다
< li class =" nav-item " v-for =" item in data " >
< a href =" javascript:void(0) " @click =" handleTitleClick(item) " > {{ item.name }} </ a >
< template v-if =" item.groups " >
< div class =" nav-group " v-for =" group in item.groups " >
< div class =" nav-group__title " > {{ group.groupName }} </ div >
< ul class =" pure-menu-list " >
< template v-for =" navItem in group.list " >
< li class =" nav-item " v-if =" !navItem.disabled " >
< router-link
active-class =" active "
:to =" base + navItem.path "
v-text =" navItem.title " />
</ li >
</ template >
</ ul >
</ div >
</ template >
</ li >전체 코드 Side-Nav.Vue를 보려면 여기를 클릭하십시오
우리는 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 >그런 다음 페이지를 다시 방문하십시오. 결과는 그림에 나와 있습니다.
모바일 데모와 PC의 원리는 비슷합니다. 둘 다 디스플레이를 위해 nav.config.json 파일을 구문 분석해야합니다
현재 정문 페이지 MobileApp.vue 를 제외하고 모바일 터미널에는 루트 구성 요소 의존성이 없습니다. 다음으로, 우리는 루트 구성 요소의 개발을 먼저 완료하고 examples/src/components 아래에서 새로운 demo-list.vue 파일을 작성하고 일부 내용을 작성합니다.
< template >
< div class =" side-nav " >
< h1 class =" vui-title " > </ h1 >
< h2 class =" vui-desc " > VUI 移动组件库</ h2 >
</ div >
</ template > 그런 다음 경로에서 참조하고 mobile.js 파일에 작성해야합니다.
import DemoList from './components/demo-list.vue'
routesConfig . push ( {
path : '/' ,
component : DemoList
} ) 그런 다음 demo-list.vue 파일을 개선하기 시작하십시오
< template >
< div class =" side-nav " >
< h1 class =" vui-title " > </ h1 >
< h2 class =" vui-desc " > VUI 移动组件库</ h2 >
< div class =" mobile-navs " >
< div v-for =" (item, index) in data " :key =" index " >
< div class =" mobile-nav-item " v-if =" item.showInMobile " >
< mobile-nav v-for =" (group, s) in item.groups " :group =" group " :base =" base " :key =" s " > </ mobile-nav >
</ div >
</ div >
</ div >
</ div >
</ template >
< script >
import navConfig from '../nav.config.json' ;
import MobileNav from './mobile-nav' ;
export default {
data ( ) {
return {
data : navConfig [ 'zh-CN' ] ,
base : '/component'
} ;
} ,
components : {
MobileNav
}
} ;
</ script >
< style lang =" postcss " >
.side-nav {
width: 100%;
box-sizing: border-box;
padding: 90px 15px 20px;
position: relative;
z-index: 1;
.vui-title,
.vui-desc {
text-align: center;
font-weight: normal;
user-select: none;
}
.vui-title {
padding-top: 40px;
height: 0;
overflow: hidden;
background: url(https://raw.githubusercontent.com/xuqiang521/vui/master/src/assets/logo.png) center center no-repeat;
background-size: 40px 40px;
margin-bottom: 10px;
}
.vui-desc {
font-size: 14px;
color: #666;
margin-bottom: 50px;
}
}
</ style >这里我们引用了mobile-nav.vue文件,这也是我们接下来要完成的移动端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
美团点评长期招人,如果有兴趣的话,欢迎一起搞基,简历投递方式交流群中有说明~
小伙伴们你们还在等什么呢?赶紧先给文章点波赞,然后关注我一波,然后加群和大佬们一起交流啊~~~