视图(View):用户界面。 控制器(Controller):业务逻辑 模型(Model):数据保存 View 传送指令到 Controller Controller 完成业务逻辑后,要求 Model 改变状态 Model 将新的数据发送到 View,用户得到反馈
那么怎么做呢 1.模块化你的 js 代码 将同一模块的 js 代码放在同一个文件夹里并正确命名,用立即执行函数相关博客 封装代码,防止出现全局变量
1 2 3 4 5 <!-- 模块化 --> <script src="./js/init-swiper.js"></script> <script src="./js/auto-slide-up.js"></script> <script src="./js/sticky-topbar.js"></script> <script src="./js/smoothly-navigation.js"></script>
2.设置 V 和 C
2.1.首先简单区分出 V 和 C 找到 js 模块对应的 html 模块,即是 view view 的作用是告诉 js 哪一部分是对应模块的 view 以轮播模块为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!-- 这就是 view ,用户可以看到 --> <div id="mySlides"> <!-- Slider main container --> <div class="swiper-container"> <!-- Additional required wrapper --> <div class="swiper-wrapper"> <!-- Slides --> <img src="./img/works/nav-page.jpg" class="swiper-slide"> <img src="./img/works/canvas.jpg" class="swiper-slide"> <img src="./img/works/apple-style-slides.jpg" class="swiper-slide"> </div> <!-- If we need pagination --> <div class="swiper-pagination"></div> </div> <!-- If we need navigation buttons --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div>
2.1.1.在 js 中声明一个 view 作为 js 模块的 view,如轮播模块的 view 为 #mySlides 2.1.2.声明一个 controller 他是 view 的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 !function(){ var view = document.querySelector('#mySlides') var controller = function(view){ var mySwiper = new Swiper(view.querySelector('.swiper-container'), { loop: true, // If we need pagination pagination: { el: '.swiper-pagination', }, // Navigation arrows navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', } }) } controller(view) }.call()
2.2.再简化一下 用另一个模块 topNavBar 举例(可以看到每个模块结构是一致的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ! function () { var view = document.querySelector('#topNavBar') //把 controller 变成对象 var controller = { //把函数放到 init 里(init 即是初始化) init: function (view) { window.addEventListener('scroll', function (x) { if (window.scrollY > 0) { topNavBar.classList.add('sticky') } else { topNavBar.classList.remove('sticky') } }) } } //此时 controller(view) 就变成了 controller.init(view) controller.init(view) }.call()
下面是关键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ! function () { var view = document.querySelector('#topNavBar') var controller = { //开始 controller 有个空的 view view: null, //有个初始化函数 init: function (view) { //把 view 存到 controller 的 view 里 this.view = view //下面代码 this.bindEvents.call(this) this.bindEvents() }, //绑定事件 bindEvents: function () { //所以这里的 this 就是上面的 this //上面的 this 就等于 controller.init(view) //controller.init(view) 的 this 就是 controller var view = this.view window.addEventListener('scroll', function (x) { if (window.scrollY > 0) { topNavBar.classList.add('sticky') } else { topNavBar.classList.remove('sticky') } }) } } //下面代码等价于controller.init.call(controller,view) 即 this 就是controller controller.init(view) }.call()
controller 有个 view,有个初始化函数,并可以绑定事件 下面优化绑定事件函数内部代码,让其只起绑定事件的作用
1 2 3 4 5 6 7 window.addEventListener('scroll', function (x) { if (window.scrollY > 0) { topNavBar.classList.add('sticky') } else { topNavBar.classList.remove('sticky') } })
由于 addEventListener 里面的 this 代表用户触发的元素 但是我们希望 this 与原来一致解决方法一:用 bind()
1 2 3 4 5 6 7 window.addEventListener('scroll', function (x) { if (window.scrollY > 0) { topNavBar.classList.add('sticky') } else { topNavBar.classList.remove('sticky') } }).bind(this)
解决方法二: 用箭头函数,由于箭头函数没有 this ,所以当我们在其内部使用 this 默认就是外部的 this。可以说箭头函数内外 this 不变,我们的目的就是让函数内外 this 不变
然后将 addClass 和 remove Class 事件也用各自的函数分隔开,同样用 this 和 view 串起来 完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ! function () { var view = document.querySelector('#topNavBar') var controller = { view: null, init: function (view) { this.view = view this.bindEvents() }, bindEvents: function () { var view = this.view window.addEventListener('scroll', (x) =>{ if (window.scrollY > 0) { this.active() } else { this.deactive() } }) }, active:function(){ this.view.classList.add('sticky') }, deactive:function(){ this.view.classList.remove('sticky') } } controller.init(view) }.call()
轮播完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ! function () { var view = document.querySelector('#mySlides') var controller = { view: null, swiper: null, swiperOptions: { loop: true, pagination: { el: '.swiper-pagination', }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', } }, init: function (view) { this.view = view this.initSwiper() }, initSwiper: function () { this.swiper = new Swiper( this.view.querySelector('.swiper-container'), this.swiperOptions ) }, } controller.init(view) }.call()
smoothly-navigation.js 模块代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 ! function () { var view = document.querySelector('nav.menu') var controller = { view: null, aTags: null, init: function (view) { this.view = view this.initAnimation() this.bindEvents() }, initAnimation: function () { function animate(time) { requestAnimationFrame(animate); TWEEN.update(time); } requestAnimationFrame(animate); }, scrollToElement: function (element) { let top = element.offsetTop let currentTop = window.scrollY let targetTop = top - 80 let s = targetTop - currentTop //路程 var coords = { y: currentTop }; //起始位置 var t = Math.abs(s / 100) * 300; if (t > 500) { t = 500 } //时间 var tween = new TWEEN.Tween(coords) //起始位置 .to({ y: targetTop }, t) //结束位置和时间 .easing(TWEEN.Easing.Quadratic.InOut) //缓动类型 .onUpdate(function () { //coords.y 已经变了 window.scrollTo(0, coords.y) //如何更新界面 }) .start(); //开始缓动 }, bindEvents: function () { let aTags = this.view.querySelectorAll('nav.menu > ul > li > a') for (let i = 0; i < aTags.length; i++) { aTags[i].onclick = (x) =>{ x.preventDefault() let a = x.currentTarget let href = a.getAttribute('href') //'#siteAbout' let element = document.querySelector(href) this.scrollToElement(element) } } } } controller.init(view) }.call()
总结 所有模块结构:在立即执行函数内部 有个 view 有个 controller controller 操作 view 将复杂的代码模块化,然后通过对象 controller 将 view 的函数通过 this 串起来,使得每一个 view 的函数都可以被 controller 操控
后续:MVC 的 M:做一个简单的留言——leancloud 数据库
本文仅供个人学习使用