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