We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
学习制造可以在生产环境使用的轮子,Start !
开发插件最重要的一点,就是插件的兼容性,一个插件至少要能同时在几种不同的环境中运行(UMD 模块机制)。
其次,它还需要满足以下几种功能及条件:
基础模板:
(function(root, factory) { if (typeof define === "function" && define.amd) { define([], factory); } else if (typeof module === "object" && module.exports) { module.exports = factory(); } else { root.Plugin = factory(); } })(typeof self !== "undefined" ? self : this, function() { "use strict"; // tool function extend(o, n, override) { for (var p in n) { if (n.hasOwnProperty(p) && (!o.hasOwnProperty(p) || override)) o[p] = n[p]; } } // polyfill var EventUtil = { // https://www.cnblogs.com/hykun/p/EventUtil.html }; // plugin construct function function Plugin(selector, userOptions) { // Plugin() or new Plugin() if (!(this instanceof Plugin)) return new Plugin(selector, userOptions); this.init(selector, userOptions); } Plugin.prototype = { constructor: Plugin, // default option options: {}, init: function(selector, userOptions) { extend(this.options, userOptions, true); } }; return Plugin; });
要实现的分页效果是什么样的,分成两种情况,显示和不显示省略号的
第一种: 不显示省略号
// 总共30页 // 第一种情况:不显示省略号,当前页码前后最多显示2个页码 当前页码为 1,那么显示 1 2 3 4 5 当前页码为 2,那么显示 1 2 3 4 5 当前页码为 3,那么显示 1 2 3 4 5 当前页码为 4,那么显示 2 3 4 5 6 ... 当前页码为 15,那么显示 13 14 15 16 17 ... 当前页码为 27,那么显示 25 26 27 28 29 当前页码为 28,那么显示 26 27 28 29 30 当前页码为 29,那么显示 26 27 28 29 30 当前页码为 30,那么显示 26 27 28 29 30
var total = 30; for (var i = 1; i <= total; i++) { console.log(showPages(i, total, 2)); } function showPages(page, total, show) { var str = ""; // 当前页前无空余页数,不需要位移 1,2 ,3,4,5 if (page < show + 1) { for (var i = 1; i <= show * 2 + 1; i++) { str = str + " " + i; } // 当前页后无空余页数,不需要位移 26 27 28 29 30 } else if (page > total - show) { for (var i = total - show * 2; i <= total; i++) { str = str + " " + i; } // 当前页有空余页数,拥有位移 } else { for (var i = page - show; i <= page + show; i++) { str = str + " " + i; } } return str.trim(); }
打印结果:
第二种: 显示省略号
待分析...
// 模仿jQuery $() function $(selector, context) { context = arguments.length > 1 ? context : document; return context ? context.querySelectorAll(selector) : null; } var Pagination = function(selector, pageOption) { // 默认配置 this.options = { curr: 1, // 当前页码 pageShow: 2, // 当前页前后两边可显示的页码个数(选填,默认为2) ellipsis: true, // 是否显示省略号 hash: false }; // 合并配置 extend(this.options, pageOption, true); // 分页器元素 this.pageElement = $(selector)[0]; // 数据总数 this.dataCount = this.options.count; // 当前页码 this.pageNumber = this.options.curr; // 总页数 this.pageCount = Math.ceil(this.options.count / this.options.limit); // 渲染 this.renderPages(); // 执行回调函数 this.options.callback && this.options.callback({ curr: this.pageNumber, limit: this.options.limit, isFirst: true }); // 改变页数并触发事件 this.changePage(); }; Pagination.prototype = { constructor: Pagination, changePage: function() {} }; return Pagination;
基本参数
// 分页元素ID(必填) var selector = "#pagelist"; // 分页配置 var pageOption = { limit: 5, // 每页显示数据条数(必填) count: 162, // 数据总数(一般通过后端获取,必填) curr: 1, // 当前页码(选填,默认为1) ellipsis: true, // 是否显示省略号(选填,默认显示) pageShow: 2, // 当前页前后两边可显示的页码个数(选填,默认为2) hash: false, // 开启location.hash,并自定义hash值对追加 #!hash值={curr} (默认关闭) // 页面加载后默认执行一次,然后当分页被切换时再次触发 callback: function(obj) { // obj.curr:获取当前页码 // obj.limit:获取每页显示数据条数 // obj.isFirst:是否首次加载页面,一般用于初始加载的判断 // 首次不执行 if (!obj.isFirst) { // do something } } }; // 初始化分页器 new Pagination(selector, pageOption);
对分页器进行点击事件的绑定,changePage()方法,对各项点击事件的监听重新渲染 renderPages
changePage()
renderPages
期望渲染效果:
<ol class="pagination" id="pagelist"> <li class="pagination-item"> <a href="javascript:;" id="page" class="pagination-link current">1</a> </li> <li class="pagination-item"> <a href="javascript:;" id="page" class="pagination-link">2</a> </li> <li class="pagination-item"> <a href="javascript:;" id="page" class="pagination-link">3</a> </li> <li class="pagination-item"> <a href="javascript:;" id="page" class="pagination-link">4</a> </li> <li class="pagination-item"> <a href="javascript:;" id="page" class="pagination-link">5</a> </li> <li class="pagination-item"> <a href="javascript:;" id="next" class="pagination-link">后一页</a> </li> <li class="pagination-item"> <a href="javascript:;" id="last" class="pagination-link">尾页</a> </li> </ol>
在这里仅分析下没有省略号的情况,renderPages渲染页面实际执行下面的 renderNoEllipsis方法
renderNoEllipsis
renderNoEllipsis: function() { var fragment = document.createDocumentFragment(); // 处理当前 pageNumber 刚开始,没有位移的情况 if (this.pageNumber < this.options.pageShow + 1) { fragment.appendChild(this.renderDom(1, this.options.pageShow * 2 + 1)); // 处理当前 pageNumber 快结束,没有位移的情况 } else if (this.pageNumber > this.pageCount - this.options.pageShow) { fragment.appendChild( this.renderDom( this.pageCount - this.options.pageShow * 2, this.pageCount ) ); } else { // 有位移 fragment.appendChild( this.renderDom( this.pageNumber - this.options.pageShow, this.pageNumber + this.options.pageShow ) ); } // 加首页以及前一页 if (this.pageNumber > 1) { this.addFragmentBefore(fragment, [ this.pageInfos[0], this.pageInfos[1] ]); } // 加后一页和尾页 if (this.pageNumber < this.pageCount) { this.addFragmentAfter(fragment, [this.pageInfos[2], this.pageInfos[3]]); } return fragment; },
核心工具函数
renderDom
for
addFragmentAfter
addFragmentBefore
createHtml
html
<li class="pagination-item"><a href="javascript:;" id="page" class="pagination-link">5</a> </li>
renderDom: function(begin, end) { var fragment = document.createDocumentFragment(); var str = ""; for (var i = begin; i <= end; i++) { // str 为 clasName str = this.pageNumber === i ? CLASS_NAME.LINK + " current" : CLASS_NAME.LINK; this.addFragmentAfter(fragment, [this.getPageInfos(str, i)]); } return fragment; } addFragmentBefore: function(fragment, datas) { fragment.insertBefore(this.createHtml(datas), fragment.firstChild); } addFragmentAfter: function(fragment, datas) { fragment.appendChild(this.createHtml(datas)); } createHtml: function(elemDatas) { // [ { className: "pagination-link current", content: 1, id: "page" } ]; var self = this; var fragment = document.createDocumentFragment(); var liEle = document.createElement("li"); var aEle = document.createElement("a"); elemDatas.forEach(function(v, index) { liEle.setAttribute("class", CLASS_NAME.ITEM); aEle.setAttribute("href", "javascript:;"); aEle.setAttribute("id", v.id); if (v.id !== 'page') { aEle.setAttribute("class", CLASS_NAME.LINK); } else { aEle.setAttribute("class", v.className); } aEle.innerHTML = v.content; liEle.appendChild(aEle); fragment.appendChild(liEle); }); return fragment; }
细节优化:document.createDocumentFragment API, 创建一个临时占位符再插入 DOM,避免 DOM 操作时的重绘和回流,提高页面性能
document.createDocumentFragment
环境配置
采用 webpack打包构建流程:
webpack
./src/es6/
index.js
Babel
// webpack.config.js const path = require("path"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); const CleanWebpackPlugin = require("clean-webpack-plugin"); //每次构建清理dist目录 module.exports = { // 模式配置 mode: "development", // 入口文件 entry: { pagination: "./src/es6/index.js" }, // 出口文件 output: { path: path.resolve(__dirname, "dist-es6"), filename: "csdwheels.min.js", libraryTarget: "umd", library: "csdwheels" }, // 对应的插件 plugins: [ new CleanWebpackPlugin(["dist-es6"]), new UglifyJsPlugin({ test: /\.js($|\?)/i }) ], // 开发服务器配置 devServer: {}, // 处理对应模块 module: { rules: [ { test: /\.js$/, include: path.join(__dirname, "src/es6"), exclude: /node_modules/, use: ["babel-loader"] }, { test: /\.scss$/, use: [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "sass-loader" } ] } ] } };
代码重构
使用 ES6 中的 Class重构代码,源码
Class
梳理改造过程:
import "../../../style/pagination/pagination.scss"; class Pagination { static PAGE_INFOS = [ { id: "first", content: "首页" }, { id: "prev", content: "前一页" }, { id: "next", content: "后一页" }, { id: "last", content: "尾页" }, { id: "", content: "..." } ]; constructor(selector, options = {}) { // 默认配置 this.options = { curr: 1, pageShow: 2, ellipsis: true, hash: false }; Object.assign(this.options, options); this.init(selector); } changePage() {} init(selector) { // 分页器元素 this.pageElement = this.$(selector)[0]; // 数据总数 this.dataCount = this.options.count; // 当前页码 this.pageNumber = this.options.curr; // 总页数 this.pageCount = Math.ceil(this.options.count / this.options.limit); // 渲染 this.renderPages(); // 执行回调函数 this.options.callback && this.options.callback({ curr: this.pageNumber, limit: this.options.limit, isFirst: true }); // 改变页数并触发事件 this.changePage(); } } export default Pagination;
补充:ES7 新提案的 static 语法,添加 babel 语法转换 npm i babel-preset-stage-0 -D
npm i babel-preset-stage-0 -D
class Pager { constructor(options) { let defaultOptions = { element: null, buttonCount: 10, currentPage: 1, totalPage: 1, pageQuery: "", // 'page' templates: { number: "<span>%page%</span>", prev: "<button class=prev>上一页</button>", next: "<button class=next>下一页</button>", first: "<button class=first>首页</button>", last: "<button class=last>末页</button>" } }; this.options = Object.assign({}, defaultOptions, options); this.domRefs = {}; this.currentPage = parseInt(this.options.currentPage, 10) || 1; this.checkOptions() .initHtml() .bindEvents(); } checkOptions() {} bindEvents() {} goToPage(page) { if (!page || page > this.options.totalPage || page === this.currentPage) { return; } this.currentPage = page; this.rerender(); } rerender() { this._checkButtons(); let newNumbers = this._createNumbers(); let oldNumbers = this.domRefs.numbers; oldNumbers.parentNode.replaceChild(newNumbers, oldNumbers); this.domRefs.numbers = newNumbers; } initHtml() { let pager = (this.domRefs.pager = document.createElement("nav")); this.domRefs.next = dom.create(this.options.templates.next); this._checkButtons(); this.domRefs.numbers = this._createNumbers(); // 此处省略 first prev last pager.appendChild(this.domRefs.numbers); pager.appendChild(this.domRefs.next); this.options.element.appendChild(pager); return this; } _checkButtons() {} _createNumbers() { let currentPage = this.currentPage; let { buttonCount, totalPage } = this.options; let start1 = Math.max(currentPage - Math.round(buttonCount / 2), 1); let end1 = Math.min(start1 + buttonCount - 1, totalPage); let end2 = Math.min( currentPage + Math.round(buttonCount / 2) - 1, totalPage ); let start2 = Math.max(end2 - buttonCount + 1, 1); let start = Math.min(start1, start2); let end = Math.max(end1, end2); let ol = dom.create('<ol data-role="pageNumbers"></ol>'); let numbers = []; for (var i = start; i <= end; i++) { let li = dom.create( `<li data-page="${i}">${this.options.templates.number.replace( "%page%", i )}</li>` ); if (i === currentPage) { li.classList.add("current"); } ol.appendChild(li); } return ol; } }
待更新...
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
学习制造可以在生产环境使用的轮子,Start !
插件设计分析
开发插件最重要的一点,就是插件的兼容性,一个插件至少要能同时在几种不同的环境中运行(UMD 模块机制)。
其次,它还需要满足以下几种功能及条件:
基础模板:
思路分析
要实现的分页效果是什么样的,分成两种情况,显示和不显示省略号的
第一种: 不显示省略号
打印结果:
第二种: 显示省略号
待分析...
基本架构
基本参数
事件绑定
对分页器进行点击事件的绑定,
changePage()
方法,对各项点击事件的监听重新渲染renderPages
渲染 DOM
期望渲染效果:
在这里仅分析下没有省略号的情况,
renderPages
渲染页面实际执行下面的renderNoEllipsis
方法核心工具函数
renderDom
,DOM 操作实现 思路分析中的for
循环:addFragmentAfter
DOM 结构向后插入,向前插入addFragmentBefore
createHtml
生成单个渲染页码html
结构<li class="pagination-item"><a href="javascript:;" id="page" class="pagination-link">5</a> </li>
ES6 升级
环境配置
采用
webpack
打包构建流程:./src/es6/
目录下面的index.js
项目入口文件Babel
编译它及它所引用的所有依赖(如 Scss、css 文件等)代码重构
使用 ES6 中的
Class
重构代码,源码梳理改造过程:
FrankFang 的 分页分析
Vue 插件版本
待更新...
参考
The text was updated successfully, but these errors were encountered: