胖蔡叨叨叨
你听我说

Vue中 v-if和v-else 的使用

胖蔡阅读(121)

使用



<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <p v-if="show">显示IF分支</p>
            <p v-else>IF分支隐藏了</p>
        </div>
    </body>
    <script>
        new Vue({
            el:'#app',
            data:{
                show:true,
            }
        })
    </script>

显示

显示IF分支

Vue的elementUI实现自定义主题

胖蔡阅读(95)

本文转载自:https://blog.csdn.net/wangcuiling_123/article/details/78513245

使用vue开发项目,用到elementUI,根据官网的写法,我们可以自定义主题来适应我们的项目要求,下面来介绍一下两种方法实现的具体步骤,(可以参考官方文档自定义主题官方文档),先说项目中没有使用scss编写,用主题工具的方法(使用的较多)

使用命令行主题工具

使用vue-cli安装完项目并引入element-ui(具体可参考第二种方法中的介绍)

安装工具

  1. 安装主题工具
npm i element-theme -g
  1. 安装chalk主题,可以从 npm 安装或者从 GitHub 拉取最新代码
  • 从 npm
npm i element-theme-chalk -D
  • 从 GitHub
npm i https://github.com/ElementUI/theme-chalk -D

初始化变量文件

et -i [可以自定义变量文件,默认为element-variables.scss]

> ✔ Generator variables file

这时根目录下会产生element-variables.scss(或自定义的文件),大致如下:

$--color-primary: #409EFF !default;
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */
$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */
$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */
$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */

$--color-success: #67c23a !default;
$--color-warning: #eb9e05 !default;
$--color-danger: #fa5555 !default;
$--color-info: #878d99 !default;

...

修改变量

直接编辑 element-variables.scss 文件,例如修改主题色为自己所需要的颜色(如: 紫色(purple))

$--color-primary: purple;

编译主题

修改完变量后,要编译主题(如果编译后,再次修改了变量,需要重新编译)

et

> ✔ build theme font
> ✔ build element theme

引入自定义主题

最后一步,将编译好的主题文件引入项目(编译的文件默认在根目录下的theme文件下,也可以通过 -o 参数指定打包目录),在入口文件main.js中引入

import '../theme/index.css'
import ElementUI from 'element-ui'
import Vue from 'vue'

Vue.use(ElementUI)

在项目中写些样式,看下主题色是否改变:(主题色变为紫色)

<div>
      <el-button>默认按钮</el-button>
      <el-button type="primary">主要按钮</el-button>
      <el-button type="success">成功按钮</el-button>
      <el-button type="info">信息按钮</el-button>
      <el-button type="warning">警告按钮</el-button>
      <el-button type="danger">危险按钮</el-button>
    </div>

直接修改element样式变量

在项目中直接修改element的样式变量,(前提是你的文档也是使用scss编写)

首先用vue-cli安装一个新项目:

  1. 安装vue:
npm i -g vue
  1. 在项目目录下安装vue-cli:
npm i -g vue-cli
  1. 基于webpack建立新项目( vue-project)
vue init webpack vue-project
  1. 依次输入以下命令行,运行vue-project
cd vue-project
npm i
npm run dev

安装elementUI以及sass-loader,node-sass(项目中使用scss编写需要依赖的插件)

  1. 安装element-ui
npm i element-ui -S
  1. 安装sass-loader,node-sass
npm i sass-loader node-sass -D

在这里说一下,不需要配置webpack.base.conf.js文件,vue-loader会根据不同类型文件来配置相应loader来打包我们的样式文件(感兴趣的可看下vue-loader的核心代码)

改变element样式变量

  1. 在src下建立element-variables.scss文件(名字可以自定义),写入如下代码:
/* 改变主题色变量 */
$--color-primary: teal;

/* 改变 icon 字体路径变量,必需 */
$--font-path: '../node_modules/element-ui/lib/theme-chalk/fonts';

@import "../node_modules/element-ui/packages/theme-chalk/src/index";
  1. 在入口文件main.js中引入上面的文件即可
import Vue from 'vue'
import Element from 'element-ui'
import './element-variables.scss'

Vue.use(Element)

看下效果吧,在文件里引入些样式看看,如button

<div>
      <el-button>默认按钮</el-button>
      <el-button type="primary">主要按钮</el-button>
      <el-button type="success">成功按钮</el-button>
      <el-button type="info">信息按钮</el-button>
      <el-button type="warning">警告按钮</el-button>
      <el-button type="danger">危险按钮</el-button>
    </div>

默认的颜色已经变为我们自定义的了,有其他的改变在element-variable.scss文件中改变变量即可

Gulp 通过gulp-preprocess实现简单的环境配置部署

胖蔡阅读(120)

通过Gulp可以对于前端代码进行一个代码的多页面构建,避免不必要的重复性工作,提高代码的可读性,为了实现通过参数控制不同环境的打包编译,我们引入了gulp-preprocess插件,该插件在gulp代码编译期间就实现了分环境配置的功能。

配置

  1. 安装gulp-preprocess插件
$ yarn add -D gulp-preprocess 
# or
$ npm install --save-dev gulp-preprocess 
  1. gulpfile.js配置
const preprocess = require('gulp-preprocess');

......
/* 打包babel的js文件 */
gulp.task('js:dev', async function () {
  return await gulp.src(['src/js/**/*.js'])
    .pipe(preprocess({
      context: {
        // 此处可接受来自调用命令的 NODE_ENV 参数,默认为 development 开发测试环境
        NODE_ENV: process.env.NODE_ENV || 'development',
      },
    }))
    .pipe(babel())
    .pipe(uglifyJs())
    .pipe(gulp.dest('dist/js'))
})

gulp.task('dev', gulp.series(gulp.parallel('js:dev', 'css:dev', 'img:dev',  'html:dev'), function (done) {
  done()
}))
  1. package.json 添加脚本配置

...
  "scripts": {
    "dev": "gulp dev",
  },

...

添加配置文件

//config.js gulp-preprocess 通过注解方式实现了编译配置

var config = {
    // @if NODE_ENV = 'production'
    apiUrl:'http://production.api.cn',
    // @endif
    // @if NODE_ENV = 'development'
    apiUrl:'http://development.api.cn',
    // @endif
}

window.config = config;


//request.js
 $.ajax({
    type: "POST",
    dataType: "json",
    url: config.apiUrl + url,
    success: function (res) {
        console.log('请求成功:',res)
    },
    error: function (error) {
            console.log('请求失败:',error)
    },
    });

运行

$yarn run dev
#or
$npm run dev

【JavaScript】Pseudo-classical模式

胖蔡阅读(89)

在pseudo-classical模式中,对象是通过构造函数创建的,并且它的方法是直接被放到prototype中的。

pseudo-classical模式也应用在一些框架中,例如Google Closure Library, Native JavaScript objects等。

 
Pseudo-class 声明

“pseudo-class”这个词其实不是很准确,因为与PHP, Java,C++等语言相比,javaScript事实上没有类,但是pseudo-class模式在某种程度上是与它们相近的。

这篇文章假设你已经对原型继承的机制很了解了,如果不了解请查看原型继承这篇文章。

一个pseudo-class由构造函数和方法组成。例如下面的 Animal pseudo-class,有一个sit方法和两个属性。

function Animal(name){    
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
alert(this.name + ' sits down.');
} }
var animal = new Animal('Pet');
alert(animal.canWalk); //true animal.sit(); //pet sits down alert(animal.canWalk); //false

1.   当new Animal(name) 被调用时,生成新对象的__proto__指向Animal.prototype,请看下面图片的左边部分。

2. animal.sit方法改变了实例中的animal.canWalk,因此该animal对象不能走,而其他对象仍可以走。

总结pseudo-class:

(1)方法和默认的属性是在原型中的。

(2)在prototype中的方法使用的this, 指的是当前对象,因为this的值仅仅依赖调用的上下文,因此animal.sit()将this设为animal。

在以上的两点中隐藏着一些使用的危险,请看下面的例子。

你是仓鼠农场的老大,让你下面的程序员接受一个任务去创建Hamster构造函数和原型。

Hamster应该有一个food数组来存储和found方法来操作food数组,这两个方法都被添加到了原型中。

你的程序员给你带来了以下的解决方案。代码看起来还挺好的,当你创建两个Hamster时,本来饲料是属于其中一个的,但现在两个都有了。

那该怎么解决这种问题呢?

function Hamster(){}; 
Hamster.prototype = {     
food: [],     
found: function(something){         
this.food.push(something);     
} } 

//创建一个懒惰的hamster,和一个勤快的hamster,勤快的有饲料 
 speedy = new Hamster();
lazy = new Hamster(); 
speedy.found("apple");
speedy.found("orange"); 
alert(speedy.food.length);   
 
//2alert(lazy.food.length);    
//2

解决方法:

让我们仔细分析下,speedy.found(“apple”)执行时发生了什么事?

(1)解释器查找在speedy中查找found,但是speedy是一个空对象,因此会失败。

(2)解释器到speedy.__proto__(==Hamster.prototype)查找,然后找到了found方法后执行。

(3)在先前的执行阶段,由于调用speedy.__proto__,this是被设置成speedy对象。

(4)this.food在speedy中没有被找到,但是可以在speedy.__proto__中被找到。

(5)”apple”被加到了speedy.__proto__.food中。

在__proto__中的food被改变时,却在两个hamster对象中共享了,必须要解决这个问题。

解决这个问题

为了解决这个问题,我们要确定每个hamster都有他们自己的食物,可以通过在构造函数中赋值做到。

function Hamster(){     
this.food = [],};
 Hamster.prototype = {    
 found: function(something){        
 this.food.push(something);   
  } }  
speedy = new Hamster();
lazy = new Hamster();
 speedy.found("apple");
speedy.found("orange"); 
alert(speedy.food.length);    //2alert(lazy.food.length);    //0
继承

让我们创建一个新的从animal继承的一个Rabbit类。

function Rabbit(name){     
this.name = name; 
} 
Rabbit.prototype.jump = function(){     
this.canWalk = true;    
 alert(this.name + ' jumps!');
}  
var rabbit = new Rabbit('John');

正如你所看到的,结构与Animal很相似。

为了从Animal中继承,我们需要Rabbit.prototype.__proto__ == Animal.prototype. 这是一个自然的想法,因为如果一个方法不能在Rabbit.prototype中找到,我们可以在Animal.prototype中去寻找。具体看下图说明。

768998df1a9f841
1453496fb616ebf

为了实现这条链,我们需要创建一个从Animal.prototype继承来的Rabbit.prototype空对象,然后再向其中添加方法。

function Rabbit(name){    
 this.name = name; }
Rabbit.prototype = inherit(Animal.prototype); 
Rabbit.prototype.jump = function(){...}

在上面的代码中,inherit是一个通过给定的__proto__属性来创建空对象的方法。

function inherit(proto){     
function F(){};    
 F.prototype = proto;     
return new F; }

最后两个对象完整的代码如下所示。

function Animal(name){   
  this.name = name;} 
Animal.prototype = {    
 canWalk: true,     
sit: function(){        
 this.canWalk = false;        
alert(this.name + ' sits down.');     
} }  
 function Rabbit(name){    
 this.name = name; } 
//继承
Rabbit.prototype = inherit(Animal.prototype); //Rabbit方法Rabbit.prototype.jump = function(){       
 this.canWalk = true;     
alert(this.name + ' jumps!'); } 
var rabbit = new Rabbit('Sniffer'); 
rabbit.sit();    //Sniffer sitsrabbit.jump()    //sniffer jumps!    
function inherit(proto){  
   function F(){};    
 F.prototype = proto;   
  return new F; 
}

不要通过new Animal()来继承。这种方法应用得非常多,但是通过Rabbit.prototype = new Animal()是一种错误的继承方式。因为new Animal()这种方式并没有传递参数name,构造函数也许严格限制需要传递参数才行,所以这种情况下使用这种继承方式是不行的。
事实上,我们只想从Animal中继承,而不是去创建一个Animal实例吧?这才符合继承的实质。所以Rabbit.prototype = inherit(Animal.prototype)是更好点。

调用父类的构造函数

”superclass”构造函数不会被自动调用,我们可以通过apply来实现。

function Rabbit(name){  
   Animal.apply(this,arguments);
}

在当前对象的上下文执行Animal构造函数,从而达到改变name值的目的。

重写方法(多态)

为了重写父类方法,在子类的原型中代替它。

Rabbit.prototype.sit = function(){    
 alert(this.name + ' sits in a rabbity way.');
}

当调用rabbit.sit()时,搜索链为:rabbit -> Rabbit.prototype -> Animal.prototype,如果在Rabbit.prototype中找到,就会停止去Animal.prototype寻找。

当然我们也可以直接在对象中重写方法。

rabbit.sit = function(){       
 alert('a special sit of this very rabbit '+ this.name); 
}
重写后再调用父类的方法

当一个方法被重写时,我们还想调用之前老的方法,如果可能我们可以直接请求父类的prototype来实现。

Rabbit.prototype.sit = function() {    
alert('calling superclass sit:')    
Animal.prototype.sit.apply(this, arguments)
}

所有父类方法通过apply/call方法传递当前对象this来调用,Animal.prototype.sit()调用将 Animal.prototype作为this。

去除对父类的直接引用

在上面的例子当中,我们可以通过Animal.apply...或者Animal.prototype.sit.apply....直接调用父类。

正常情况下,我们不应该那样做。以后重构代码也许会改变父类的名字,或者在层级中引入新的类。

一般来说,程序语言允许调用父类方法通过使用特别的关键字,如parent.method()或者 super()。

javaScript没有这样的特性,但我们可以模拟它。

下面的extend方法可以实现继承,却不需要直接指向父类的引用。

function extend(child, parent) {     
child.prototype = inherit(parent.prototype);     
child.prototype.constructor = child;     
child.parent = parent.prototype; 
}

用法:

function Rabbit(name){     
Rabbit.parent.constructor.apply(this.arguments);    //调用父类构造器} extend(Rabbit,Animal); 
Rabbit.prototype.run = function(){     Rabbit.parent.run.apply(this,arguments); //调用父类方法 
}

事实上现在我们可以重命名Animal或者创建一个中间类GrassEatingAnimal,但是实际情况的改变只会涉及到Animal和extend(...)。

private或protected方法(封装)

可保护的方法和属性通过命名的习惯来支持。因此,一个以下划线开始的方法不应该被外面直接调用。

768998df1a9f841
9e5cc780fb8fe9f

private方法经常不支持。

requireJS缓存问题

胖蔡阅读(96)

可以在requirejs配置的入口文件里配置参数设置版本缓存:

// html

  <script type="text/javascript" data-main="/js/main" src="/js/lib/require.js"></script>

// main.js,v是版本号

require.config({
    urlArgs: "r="+v,
    map: {
        '*': {
            'css': '/js/lib/css.min.js'
        },
    },
    paths: {
        "jquery": '/js/lib/jquery-3.4.1.min',
        "es6-promise": '/js/lib/es6-promise.auto.min',
    },
    shim: {
        //     'jquery': ['css!../../css/public.css', 'css!../../css/index.css']
        "paging": {
            deps: ['query']
        }
    },
    waitSeconds: 0
});

如上,通过设置v来控制版本缓存。

RequireJs加载程序插件

胖蔡阅读(80)

RequireJS支持加载程序插件。这是一种支持依赖关系的方法,该依赖关系不是普通的JS文件,但对于脚本在执行工作之前已加载仍然很重要。RequireJS Wiki有一个插件列表。本节讨论与RequireJS一起维护的一些特定插件:

指定文本文件依赖性

使用常规HTML标记构建HTML很好,而不是在脚本中构建DOM结构。但是,没有很好的方法将HTML嵌入JavaScript文件中。最好的方法是使用HTML字符串,但这可能很难管理,尤其是对于多行HTML。

RequireJS有一个插件text.js,可以帮助解决此问题。如果文本将自动加载!前缀用于依赖项。有关更多信息,请参见 text.js自述文件

页面加载事件支持/ DOM准备就绪

使用RequireJS足够快地加载脚本以使其在DOM准备好之前完成时,这是可能的。任何尝试与DOM交互的工作都应等待DOM准备就绪。对于现代浏览器,这是通过等待DOMContentLoaded事件来完成的。

但是,并非所有使用中的浏览器都支持DOMContentLoaded。domReady模块实现了跨浏览器方法来确定DOM准备就绪的时间。下载模块,并在您的项目中使用它,如下所示:

require(['domReady'], function (domReady) {
  domReady(function () {
    //This function is called once the DOM is ready.
    //It will be safe to query the DOM and manipulate
    //DOM nodes in this function.
  });
});

由于DOM ready是常见的应用程序需求,因此理想情况下可以避免上面API中的嵌套函数。domReady模块还实现了Loader Plugin API,因此您可以使用loader插件语法(注意domReady依赖项中的)来强制require()回调函数在执行之前等待DOM准备就绪。

domReady

用作加载程序插件时,将返回当前文档:

require(['domReady!'], function (doc) {
    //This function is called once the DOM is ready,
    //notice the value for 'domReady!' is the current
    //document.
});

注意:如果文档加载时间较长(可能是非常大的文档,或者HTML脚本标签加载了大型JS文件,这些文件会阻止DOM完成直到完成),那么将domReady用作加载程序插件可能会导致RequireJS“超时”错误。如果这是一个问题,请增加waitSeconds配置,或者仅使用domReady作为模块并在require()回调内调用domReady()。

定义一个I18N捆绑

一旦您的Web应用达到了一定的大小和受欢迎程度,在界面中本地化字符串并提供其他特定于语言环境的信息就变得更加有用。但是,制定一个可以很好地扩展以支持多个语言环境的方案可能很麻烦。

RequireJS允许您设置具有本地化信息的基本模块,而无需强制您预先提供所有特定于语言环境的信息。它可以随时间添加,并且只能在特定于语言环境的文件中定义在语言环境之间更改的字符串/值。

i18n.js插件提供了对i18n包的支持。当模块或依赖项指定i18n时,它将自动加载!前缀(下面有更多信息)。下载插件,并将其放在应用程序的主JS文件所在的目录中。

要定义包,请将其放在名为“ nls”的目录中-i18n!插件假定模块名称中带有“ nls”,表示一个i18n软件包。名称中的“ nls”标记告诉i18n插件在哪里可以看到语言环境目录(它们应该是nls目录的直接子级)。如果要在“我的”模块集中提供一组颜色名称,请按以下方式创建目录结构:

  • 我/nls/colors.js

该文件的内容应如下所示:

//my/nls/colors.js contents:
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    }
});

属性为“ root”的对象文字定义了此模块。您要做的就是为以后的本地化工作奠定基础。

然后,可以在另一个模块(例如,my / lamps.js文件)中使用上述模块:

//Contents of my/lamps.js
define(["i18n!my/nls/colors"], function(colors) {
    return {
        testMessage: "The name for red in this locale is: " + colors.red
    }
});

my / lamps模块具有一个名为“ testMessage”的属性,该属性使用colors.red来显示红色的本地化值。

稍后,当您想向文件添加特定的翻译时,例如使用fr-fr语言环境,请将my / nls / colors更改为如下所示:

//Contents of my/nls/colors.js
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    },
    "fr-fr": true
});

然后在my / nls / fr-fr / colors.js定义一个文件,该文件包含以下内容:

//Contents of my/nls/fr-fr/colors.js
define({
    "red": "rouge",
    "blue": "bleu",
    "green": "vert"
});

RequireJS将使用浏览器的navigator.languages,navigator.language或navigator.userLanguage属性来确定要用于my / nls / colors的语言环境值,因此您的应用程序不必更改。如果您希望设置语言环境,则可以使用模块配置将语言环境传递给插件:

requirejs.config({
    config: {
        //Set the config for the i18n
        //module ID
        i18n: {
            locale: 'fr-fr'
        }
    }
});

请注意,RequireJS将始终使用小写版本的语言环境,以避免出现大小写问题,因此,i18n软件包的磁盘上的所有目录和文件都应使用小写语言环境。

RequireJS也足够聪明,可以选择正确的语言环境包,该语言包与my / nls / colors提供的语言包最匹配。例如,如果语言环境是“ en-us”,则将使用“ root”捆绑软件。如果区域设置为“ fr-fr-paris”,则将使用“ fr-fr”捆绑包。

RequireJS还将捆绑包合并在一起,因此,例如,如果法式捆绑包是这样定义的(将红色值省略):

//Contents of my/nls/fr-fr/colors.js
define({
    "blue": "bleu",
    "green": "vert"
});

然后将使用“ root”中的red值。这适用于所有语言环境。如果定义了下面列出的所有捆绑包,那么RequireJS将按照以下优先级顺序使用值(顶部的优先级最高):

  • 我/nls/fr-fr-paris/colors.js
  • 我/nls/fr-fr/colors.js
  • 我/nls/fr/colors.js
  • 我/nls/colors.js

如果您不希望在顶级模块中包含根包,则可以像普通的语言环境包一样定义它。在这种情况下,顶层模块如下所示:

//my/nls/colors.js contents:
define({
    "root": true,
    "fr-fr": true,
    "fr-fr-paris": true
});

根束看起来像:

//Contents of my/nls/root/colors.js
define({
    "red": "red",
    "blue": "blue",
    "green": "green"
});

RequireJs 高级用法

胖蔡阅读(120)

从包中加载模块

RequireJS支持加载CommonJS Packages目录结构中的模块,但是需要指定一些其他配置才能使其正常工作。具体来说,它支持以下CommonJS Packages功能:

  • 软件包可以与模块名称/前缀关联。
  • 程序包配置可以为特定程序包指定以下属性:
    • name:程序包的名称(用于模块名称/前缀映射)
    • location:磁盘上的位置。位置相对于baseUrl配置值,除非它们包含协议或以反斜杠(/)开头。
    • main:某人对“ packageName”的要求时应使用的包内模块的名称。默认值为“ main”,因此仅当它不同于默认值时才指定它。该值是相对于包文件夹的。

重要笔记

  • 尽管软件包可以具有CommonJS目录布局,但模块本身应采用RequireJS可以理解的模块格式。规则的例外:如果您使用的是r.js节点适配器,则这些模块可以采用传统的CommonJS模块格式。如果需要将传统的CommonJS模块转换为RequireJS使用的异步模块格式,则可以使用CommonJS转换工具
  • 一次只能在项目上下文中使用软件包的一个版本。您可以使用RequireJS多版本支持来加载两个不同的模块上下文,但是如果要在一个上下文中使用程序包A和B,并且它们依赖于程序包C的不同版本,那么这将是一个问题。将来可能会改变。

如果您使用《入门指南》中指定的类似项目布局,则Web项目的开始将类似于以下内容(基于Node / Rhino的项目是类似的,只需使用scripts目录的内容作为顶级项目目录):

  • 项目目录/
    • project.html
    • 脚本/
      • require.js

这是带有两个包cartstore的示例目录布局的外观:

  • 项目目录/
    • project.html
    • 脚本/
      • 大车/
        • main.js
      • 店铺/
        • main.js
        • util.js
      • main.js
      • require.js

project.html将具有如下脚本标记:

<script data-main="scripts/main" src="scripts/require.js"></script>

这将指示require.js加载脚本/main.js。main.js使用“ packages”配置来设置与require.js相关的软件包,在这种情况下,它们是源文件“ cart”和“ store”:

//main.js contents
//Pass a config object to require
require.config({
    "packages": ["cart", "store"]
});

require(["cart", "store", "store/util"],
function (cart,   store,   util) {
    //use the modules as usual.
});

“ cart”的需求意味着它将从scripts / cart / main.js加载,因为“ main”是RequireJS支持的默认主模块设置。将从scripts / store / util.js加载“ store / util”的需求。

如果“ store”包未遵循“ main.js”约定,则看起来更像这样:

  • 项目目录/
    • project.html
    • 脚本/
      • 大车/
        • main.js
      • 店铺/
        • store.js
        • util.js
      • main.js
      • package.json
      • require.js

然后,RequireJS配置将如下所示:

require.config({
    packages: [
        "cart",
        {
            name: "store",
            main: "store"
        }
    ]
});

为避免冗长,强烈建议始终使用在结构中使用“主要”约定的软件包。

多版本支持

配置选项中所述,可以使用不同的“上下文”配置选项将模块的多个版本加载到页面中。require.config()返回一个使用上下文配置的require函数。这是一个加载两个不同版本的alpha和beta模块的示例(此示例摘自其中一个测试文件):

<script src="../require.js"></script>
<script>
var reqOne = require.config({
  context: "version1",
  baseUrl: "version1"
});

reqOne(["require", "alpha", "beta",],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 1
  log("beta version is: " + beta.version); //prints 1

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version1 omega loaded with version: " +
             omega.version); //prints 1
      }
    );
  }, 100);
});

var reqTwo = require.config({
      context: "version2",
      baseUrl: "version2"
    });

reqTwo(["require", "alpha", "beta"],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 2
  log("beta version is: " + beta.version); //prints 2

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version2 omega loaded with version: " +
            omega.version); //prints 2
      }
    );
  }, 100);
});
</script>

注意,“ require”被指定为模块的依赖项。这允许传递给函数回调的require()函数使用正确的上下文正确加载模块以支持多版本。如果未将“ require”指定为依赖项,则可能会出现错误。

页面加载后加载代码

上面“ Multiversion支持”部分中的示例显示了以后如何通过嵌套的require()调用来加载代码。

网络工作者支持

从0.12版开始,RequireJS可以在Web Worker中运行。只需在网络工作者中使用importScripts()来加载require.js(或包含require()定义的JS文件),然后调用require。

您可能需要设置baseUrl 配置选项,以确保require()可以找到要加载的脚本。

通过查看单元测试中使用的文件之一,可以看到其用法示例。

犀牛支持§4.5

RequireJS可以通过r.js适配器在Rhino中使用。有关更多信息,请参见r.js自述文件

Nashorn支持

从RequireJS 2.1.16开始,RequireJS可以通过r.js适配器在Java 8+的JavaScript引擎Nashorn中使用。有关更多信息,请参见r.js自述文件

处理错误

错误的一般类别是脚本(未找到),网络超时或所加载脚本中的错误的404。RequireJS有一些用于处理它们的工具:特定于需求的errback,“ paths”数组配置以及全局的requirejs.onError。

传递给errbacks的错误对象和全局requirejs.onError函数通常将包含两个自定义属性:

  • requireType:具有一般分类的字符串值,例如“ timeout”,“ nodefine”,“ scripterror”。
  • requireModules:超时的模块名称/ URL的数组。

如果您在requireModules中遇到错误,则可能意味着未定义依赖于requireModules数组中的模块的其他模块。

捕获IE中的负载故障

Internet Explorer存在一系列问题,使得难以检测到错误/路径后备的加载失败:

  • script.onerror在IE 6-8中不起作用。无法知道加载脚本是否会生成404,更糟糕的是,即使在404情况下,它也会触发具有完整状态的onreadystatechange。
  • script.onerror确实可以在IE 9+中运行,但是存在一个错误,即在执行脚本后不立即触发script.onload事件处理程序,因此它不支持允许匿名AMD模块的标准方法。因此仍使用script.onreadystatechange。但是,在script.onerror函数启动之前,onreadystatechange会以完整状态启动。

因此,使用IE很难同时允许匿名的AMD模块和可靠的检测错误,匿名的AMD模块是AMD模块的核心优势。

但是,如果您知道在一个项目中使用define()声明其所有模块,或者它使用shim config为不使用define()的任何内容指定字符串导出,那么如果您设置了defineDefine配置值确实,加载器可以通过检查define()调用或填充程序的导出全局值的存在来确认脚本是否已加载。

因此,如果要支持Internet Explorer,捕获负载错误并通过直接define()调用或shim config获得模块化代码,请始终将forcedDefine设置为true。有关示例,请参见下一部分。

注意:如果您确实设置了forceDefine:true,并且使用data-main =“”来加载主JS模块,则该主JS模块必须调用define()而不是require()来加载所需的代码。JS主模块仍然可以调用require / requirejs来设置配置值,但是对于加载模块,它应该使用define()。

如果然后您还使用杏仁来构建没有require.js的代码,请确保使用insertRequire构建设置为主模块插入一个require调用-达到与最初的require()调用相同的目的,即调用data-main做。

require([])错误

当与requirejs.undef()一起使用时,Errbacks将允许您检测模块是否无法加载,取消定义该模块,将配置重置到另一个位置,然后重试。

一个常见的用例是使用CDN托管的库版本,但是如果失败,请切换到本地加载文件:

requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
    }
});

//Later
require(['jquery'], function ($) {
    //Do something with $ here
}, function (err) {
    //The errback, error callback
    //The error has a list of modules that failed
    var failedId = err.requireModules && err.requireModules[0];
    if (failedId === 'jquery') {
        //undef is function only on the global requirejs object.
        //Use it to clear internal knowledge of jQuery. Any modules
        //that were dependent on jQuery and in the middle of loading
        //will not be loaded yet, they will wait until a valid jQuery
        //does load.
        requirejs.undef(failedId);

        //Set the path to jQuery to local path
        requirejs.config({
            paths: {
                jquery: 'local/jquery'
            }
        });

        //Try again. Note that the above require callback
        //with the "Do something with $ here" comment will
        //be called if this new attempt to load jQuery succeeds.
        require(['jquery'], function () {});
    } else {
        //Some other error. Maybe show message to the user.
    }
});

使用`requirejs.undef()`,如果您稍后设置其他配置并尝试加载相同的模块,则加载器仍会记住哪些模块需要该依赖关系,并在新配置的模块加载时完成加载。

注意:errbacks仅适用于回调样式的require调用,而不适用define()调用。define()仅用于声明模块。

路径配置后备

上面的用于检测负载故障,对模块进行undef(),修改路径和重新加载的模式是一个足够常见的请求,它也有一个简写。路径配置允许使用数组值:

requirejs.config({
    //To get timely, correct error triggers in IE, force a define/shim exports check.
    enforceDefine: true,
    paths: {
        jquery: [
            'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',
            //If the CDN location fails, load from this location
            'lib/jquery'
        ]
    }
});

//Later
require(['jquery'], function ($) {
});

上面的代码将尝试CDN位置,但是如果失败,则退回到本地lib / jquery.js位置。

注意:路径回退仅适用于确切的模块ID匹配。这与可应用于模块ID前缀段的任何部分的常规路径配置不同。后备的目标更多是针对异常错误的恢复,而不是通用的路径搜索路径解决方案,因为它们在浏览器中效率低下。

全局requirejs.onError函数

要检测本地错误未捕获的错误,可以覆盖requirejs.onError():

requirejs.onError = function (err) {
    console.log(err.requireType);
    if (err.requireType === 'timeout') {
        console.log('modules: ' + err.requireModules);
    }

    throw err;
};

RequireJS 配置选项

胖蔡阅读(70)

在顶层HTML页面(或未定义模块的顶层脚本文件)中使用require()时,可以将配置对象作为第一个选项传递:

<script src="scripts/require.js"></script>
<script>
  require.config({
    baseUrl: "/another/path",
    paths: {
        "some": "some/v1.0"
    },
    waitSeconds: 15
  });
  require( ["some/module", "my/module", "a.js", "b.js"],
    function(someModule,    myModule) {
        //This function will be called when all the dependencies
        //listed above are loaded. Note that this function could
        //be called before the page is loaded.
        //This callback is optional.
    }
  );
</script>

您也可以从数据主入口点调用require.config ,但是要注意,数据主脚本是异步加载的。避免使用其他入口点脚本,这些脚本错误地假定data-main及其require.config将始终在脚本加载之前执行。

另外,可以require 加载require.js之前将config对象定义为全局变量,并自动应用值。此示例指定了一些要在require.js定义require()后立即加载的依赖项:

<script>
    var require = {
        deps: ["some/module1", "my/module2", "a.js", "b.js"],
        callback: function(module1, module2) {
            //This function will be called when all the dependencies
            //listed above in deps are loaded. Note that this
            //function could be called before the page is loaded.
            //This callback is optional.
        }
    };
</script>
<script src="scripts/require.js"></script>

注意:最好使用var require = {}并且不要使用 window.require = {},它将在IE中无法正确运行。

一些模式可以将配置与主模块加载分开

支持的配置选项:

baseUrl:用于所有模块查找的根路径。因此,在上面的示例中,“ my / module”的脚本标签将具有src =“ / another / path / my / module.js”。是的baseUrl装入普通.js文件时使用的(由一个依赖字符串指示开始以斜线,具有协议,或在端部的.js),这些字符串被原样使用,所以a.js和b.js将从与包含上述代码段的HTML页面相同的目录中加载。

如果在配置中未显式设置baseUrl,则默认值将是加载require.js的HTML页面的位置。如果使用data-main属性,则该路径将成为baseUrl。

baseUrl可以是与将加载require.js的页面不同的域上的URL。RequireJS脚本加载跨域工作。唯一的限制是文本加载的文本内容!插件:至少在开发过程中,这些路径应与页面位于同一域中。优化工具将内联文本!插件资源,因此在使用优化工具后,您可以使用引用文本的资源!来自另一个域的插件资源。

path:在baseUrl的正下方找不到模块名称的路径映射。除非路径设置以“ /”开头或其中包含URL协议(例如“ http:”),否则都假定该路径设置是相对于baseUrl的。使用上面的示例配置,“ some / module”的脚本标签将为src =“ / another / path / some / v1.0 / module.js”。

被用于模块名称应该路径包括扩展名,因为路径映射可能是一个目录。当将模块名称映射到路径时,路径映射代码将自动添加.js扩展名。如果使用了require.toUrl(),它将添加适当的扩展名(如果用于文本模板)。

在浏览器中运行时,可以指定路径回退,以允许尝试从CDN位置进行加载,但是如果CDN位置无法加载,则回退到本地位置。

bundles:在RequireJS 2.1.10中引入:允许配置要在另一个脚本中找到的多个模块ID。例子:

requirejs.config({
    bundles: {
        'primary': ['main', 'util', 'text', 'text!template.html'],
        'secondary': ['text!secondary.html']
    }
});

require(['util', 'text'], function(util, text) {
    //The script for module ID 'primary' was loaded,
    //and that script included the define()'d
    //modules for 'util' and 'text'
});

该配置指出:模块“ main”,“ util”,“ text”和“ text!template.html”将通过加载模块ID“ primary”来找到。可以通过加载模块ID“ secondary”来找到模块​​“ text!secondary.html”。

这仅设置了在脚本中包含多个define()模块的模块中查找模块的位置。它不会自动将那些模块绑定到捆绑软件的模块ID。捆绑软件的模块ID仅用于查找模块集。

使用path config可能会发生类似的事情,但是它要复杂得多,并且path config路由不允许在其配置中使用加载程序插件资源ID,因为path config的值是路径段而不是ID。

如果执行构建且该构建目标不是现有的模块ID,或者如果已构建的JS文件中包含不应由加载程序插件加载的加载程序插件资源,则bundles config很有用。请注意,键和值是模块ID,而不是路径段。它们是绝对的模块ID,而不是像path configmap config这样的模块ID前缀。此外,bundle config与map config的不同之处在于map config是一对一的模块ID关系,其中bundle config用于将多个模块ID指向bundle的模块ID。

从RequireJS 2.2.0开始,优化器可以生成bundle config,并将其插入到顶层requirejs.config()调用中。有关更多详细信息,请参见bundlesConfigOutFile构建配置选项。

shim:为不使用define()声明依赖关系和设置模块值的较旧的传统“浏览器全局变量”脚本配置依赖关系,导出和自定义初始化。

这是一个例子。它需要RequireJS 2.1.0+,并假定在baseUrl目录中已安装了ribs.js,underscore.js和jquery.js。如果没有,那么您可能需要为它们设置路径配置:

requirejs.config({
    //Remember: only use shim config for non-AMD scripts,
    //scripts that do not already call define(). The shim
    //config will not work correctly if used on AMD scripts,
    //in particular, the exports and init config will not
    //be triggered, and the deps config will be confusing
    //for those cases.
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                //Note: jQuery registers as an AMD module via define(),
                //so this will not work for jQuery. See notes section
                //below for an approach for jQuery.
                return this.Foo.noConflict();
            }
        }
    }
});

//Then, later in a separate file, call it 'MyModel.js', a module is
//defined, specifying 'backbone' as a dependency. RequireJS will use
//the shim config to properly load 'backbone' and give a local
//reference to this module. The global Backbone will still exist on
//the page too.
define(['backbone'], function (Backbone) {
  return Backbone.Model.extend({});
});

在RequireJS 2.0。*中,shim配置中的“ exports”属性可能是函数而不是字符串。在这种情况下,它的功能与上面显示的“ init”属性相同。“ init”模式在RequireJS 2.1.0+中使用,因此exports可以将字符串值用于 forceDefine,但是一旦已知已加载库,就可以进行功能工作。

对于仅仅是jQuery或Backbone插件的“模块”,不需要导出任何模块值,shim配置可以只是一个依赖项数组:

requirejs.config({
    shim: {
        'jquery.colorize': ['jquery'],
        'jquery.scroll': ['jquery'],
        'backbone.layoutmanager': ['backbone']
    }
});

但是请注意,如果要在IE中进行404负载检测,以便可以使用路径回退或errbacks,则应提供字符串输出值,以便加载程序可以检查脚本是否实际加载(init的返回用于enforceDefine检查):

requirejs.config({
    shim: {
        'jquery.colorize': {
            deps: ['jquery'],
            exports: 'jQuery.fn.colorize'
        },
        'jquery.scroll': {
            deps: ['jquery'],
            exports: 'jQuery.fn.scroll'
        },
        'backbone.layoutmanager': {
            deps: ['backbone']
            exports: 'Backbone.LayoutManager'
        }
    }
});

“ shim”配置的重要说明:

  • 填充程序配置仅设置代码关系。要加载属于shim config或使用shim config的模块,需要一个正常的require / define调用。本身设置填充程序不会触发代码加载。
  • 仅将其他“填充程序”模块用作填充脚本的依赖性,或者将不具有依赖性的AMD库也创建全局变量(如jQuery或lodash)后调用define()。否则,如果您使用AMD模块作为匀场配置模块的依赖项,则在构建之后,可能要等到构建中的填充代码执行后,才能评估该AMD模块,否则会发生错误。最终的解决方法是将所有填充的代码升级为具有可选的AMD define()调用。
  • 从RequireJS 2.1.11开始,如果无法升级填充代码以使用AMD define()调用,则优化器具有 wrapShim构建选项,该选项将尝试自动将填充代码包装在define()中以进行构建。这改变了填充的依赖关系的范围,因此不能保证始终有效,但是,例如,对于依赖于AMD版本的Backbone的填充的依赖关系,它可能会有所帮助。
  • 对于AMD模块,不会调用init函数。例如,您不能使用shim init函数来调用jQuery的noConflict。请参阅映射模块以将noConflict 用于jQuery的替代方法。
  • 通过RequireJS在节点中运行AMD模块时,不支持Shim config(尽管可用于优化程序)。根据正在填充的模块,它可能会在Node中失败,因为Node与浏览器没有相同的全局环境。从RequireJS 2.1.7开始,它将在控制台中警告您不支持shim config,并且它可能会或可能不会起作用。如果您想禁止显示该消息,则可以通过requirejs.config({ suppress: { nodeShim: true }});

“ shim”配置的重要优化器说明

  • 您应该使用mainConfigFile构建选项来指定可在其中找到垫片配置的文件。否则,优化程序将不了解填充程序配置。另一个选项是在构建配置文件中复制填充程序配置。
  • 不要在构建中将CDN加载与shim config混合使用。示例方案:从CDN加载jQuery,但使用shim配置加载类似于jQuery的Backbone的普通版本。在进行构建时,请确保在内建文件中内联jQuery,并且不要从CDN加载它。否则,Backbone将内联到生成的文件中,并在CDN加载的jQuery加载之前执行。这是因为shim config只是延迟文件的加载,直到加载依赖项为止,但不对define进行任何自动包装。构建后,依赖关系已经内联,shim配置无法将non-define()代码的执行推迟到以后。定义()’ d模块在构建后就可以处理CDN加载的代码,因为它们将其源代码正确包装在define工厂函数中,直到加载依赖项后才执行。因此,课程:shim config是非模块化代码,传统代码的权宜之计。define()的模块更好。
  • 对于本地的多文件构建,上述CDN建议也适用。对于任何匀场脚本,必须在匀场脚本执行之前加载其依赖项。这意味着要么直接在包含填充脚本的buid层中构建其依赖关系,要么通过require([], function (){})调用加载其依赖关系,然后require([])对具有填充脚本的构建层进行嵌套调用。
  • 如果您使用uglifyjs缩小代码,请不要将uglify选项设置toplevel为true,或者如果使用命令 行不通-mt。该选项会破坏shim用于查找出口的全局名称。

map:对于给定的模块前缀,而不是使用给定的ID加载模块,而是替换一个不同的模块ID。

这种功能对于大型项目而言非常重要,因为大型项目可能有两组模块需要使用两个不同的’foo’版本,但它们仍需要彼此合作。

使用上下文支持的多版本支持是不可能的。另外,路径配置仅用于设置模块ID的根路径,而不用于将一个模块ID映射到另一个模块。

地图示例:

requirejs.config({
    map: {
        'some/newmodule': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

如果模块按如下方式布置在磁盘上:

  • foo1.0.js
  • foo1.2.js
  • 一些/
    • newmodule.js
    • oldmodule.js

当’some / newmodule’执行`require(’foo’)`时,它将获取foo1.2.js文件;当’some / oldmodule’执行`require(’foo’)`时,它将获取foo1.0。 js文件。

此功能仅适用于是真正的AMD模块的脚本,这些脚本调用define()并注册为匿名模块。另外,仅对映射配置使用绝对模块ID。相对ID(如'../some/thing')不起作用。

还支持“ *”映射值,这意味着“对于所有加载的模块,请使用此映射配置”。如果有更具体的地图配置,则该配置优先于星型配置。例子:


requirejs.config({
    map: {
        '*': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

表示对于除“ some / oldmodule”以外的任何模块,当需要“ foo”时,请改用“ foo1.2”。仅对于“ some / oldmodule”,当要求“ foo”时使用“ foo1.0”。

注意:使用map config进行构建时,需要将map config馈给优化器,并且build输出必须仍然包含requirejs config调用来设置map config。优化器在构建期间不会进行ID重命名,因为项目中的某些依赖项引用可能取决于运行时变量状态。因此,优化器不会在构建后使对映射配置的需求无效。

config:通常需要将配置信息传递给模块。该配置信息通常被称为应用程序的一部分,并且需要一种将其传递给模块的方法。在RequireJS中,这是通过requirejs.config()的config选项完成的。然后,模块可以通过请求特殊的依赖项“模块”并调用module.config()来读取该信息。例子:

requirejs.config({
    config: {
        'bar': {
            size: 'large'
        },
        'baz': {
            color: 'blue'
        }
    }
});

//bar.js, which uses simplified CJS wrapping:
//https://requirejs.org/docs/whyamd.html#sugar
define(function (require, exports, module) {
    //Will be the value 'large'
    var size = module.config().size;
});

//baz.js which uses a dependency array,
//it asks for the special module ID, 'module':
//https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#wiki-magic
define(['module'], function (module) {
    //Will be the value 'blue'
    var color = module.config().color;
});

要将config传递给package,请以该包中的主模块(而不是package ID)为目标:

requirejs.config({
    //Pass an API key for use in the pixie package's
    //main module.
    config: {
        'pixie/index': {
            apiKey: 'XJKDLNS'
        }
    },
    //Set up config for the "pixie" package, whose main
    //module is the index.js file in the pixie folder.
    packages: [
        {
            name: 'pixie',
            main: 'index'
        }
    ]
});

:配置来自CommonJS包的加载模块。有关更多信息,请参见软件包主题

nodeIdCompat:节点治疗模块IDexample.jsexample相同的。默认情况下,这是RequireJS中的两个不同ID。如果最终使用的是从npm安装的模块,则可能需要设置此配置值true以避免解析问题。此选项仅适用于以不同方式处理“ .js”后缀,它不执行任何其他节点解析和评估匹配,例如.json文件处理(JSON处理仍然需要“ json!”加载程序插件)。在2.1.10及更高版本中可用。

waitSeconds:放弃放弃加载脚本之前要等待的秒数。将其设置为0将禁用超时。默认值为7秒。

context:赋予加载上下文的名称。只要每个顶级require调用指定一个唯一的上下文字符串,这都允许require.js在页面中加载模块的多个版本。要正确使用它,请参阅“ Multiversion支持”部分。

deps:要加载的依赖项数组。当在require.js加载之前将require定义为配置对象时,并且您要指定要在require()定义后立即加载的依赖项时,此选项很有用。使用deps就像进行require([])调用一样,但是在加载程序处理完配置后立即使用。它不会阻止 其他任何require()调用启动对模块的请求,它只是指定某些模块作为config块的一部分异步加载的一种方式。

callback:加载deps后执行的函数。在将require.js加载之前,将require定义为配置对象,并且您希望在加载配置的deps数组之后指定要使用的函数时,此选项很有用。

:设置为true时,如果脚本加载时未调用define()或具有可检查的填充程序导出字符串值,则将引发错误。有关更多信息,请参见在IE中捕获负载故障

xhtml:如果设置为true,则document.createElementNS()将用于创建脚本元素。

urlArgs:附加到RequireJS用于获取资源的URL的额外查询字符串参数。在未正确配置浏览器或服务器时,最有用的方法是缓存崩溃。urlArgs的高速缓存半身设置示例:

urlArgs: "bust=" +  (new Date()).getTime()

从RequireJS 2.2.0开始,urlArgs可以是一个函数。如果是函数,它将接收模块ID和URL作为参数,并且应返回将添加到URL末尾的字符串。如果没有参数,则返回一个空字符串。请务必注意添加“?” 或“&”(取决于URL的现有状态)。例子:

requirejs.config({
    urlArgs: function(id, url) {
        var args = 'v=1';
        if (url.indexOf('view.html') !== -1) {
            args = 'v=2'
        }

        return (url.indexOf('?') === -1 ? '?' : '&') + args;
    }
});

在开发过程中,使用它可能会很有用,但是请确保在部署代码之前将其删除。

scriptType:指定type =“”属性的值,该属性用于RequireJS插入文档中的脚本标签。默认值为“文本/ javascript”。要使用Firefox的JavaScript 1.8功能,请使用“ text / javascript; version = 1.8”。

skipDataMain:在RequireJS 2.1.9中引入:如果设置为true,则跳过对数据主属性的扫描以开始加载模块。如果RequireJS嵌入在可以与页面上的其他RequireJS库进行交互的实用程序库中,并且嵌入的版本不应进行数据主加载,则很有用。

RequireJs API基本使用

胖蔡阅读(93)

加载JavaScript文件

与传统的<script>标记相比,RequireJS采用了不同的脚本加载方式。尽管它还可以快速运行并优化得很好,但主要目标是鼓励使用模块化代码。作为其一部分,它鼓励使用模块ID代替脚本标记的URL。

RequireJS加载相对于baseUrl的所有代码。通常,将baseUrl设置为与data-main属性中使用的脚本相同的目录,以使顶级脚本加载页面。该数据主要属性是一个特殊的属性,require.js将检查启动脚本加载。本示例将以脚本的baseUrl结尾:

<!--This sets the baseUrl to the "scripts" directory, and
    loads a script that will have a module ID of 'main'-->
<script data-main="scripts/main.js" src="scripts/require.js"></script>

或者,可以通过RequireJS config手动设置baseUrl。如果没有显式配置且未使用data-main,则默认的baseUrl是包含运行RequireJS的HTML页面的目录。

默认情况下,RequireJS还假定所有依赖项都是脚本,因此它不希望在模块ID上看到尾随的“ .js”后缀。在将模块ID转换为路径时,RequireJS将自动添加它。使用path config,可以设置一组脚本的位置。与传统的<script>标记相比,所有这些功能都允许您为脚本使用较小的字符串。

有时您可能确实想直接引用脚本,而又不遵循“ baseUrl +路径”规则来查找脚本。如果模块ID具有以下特征之一,则该ID将不会通过“ baseUrl +路径”配置传递,而只会被视为与文档相关的常规URL:

  • 以“ .js”结尾。
  • 以“ /”开头。
  • 包含URL协议,例如“ http:”或“ https:”。

通常,最好使用baseUrl和“ paths”配置来设置模块ID的路径。这样,它为您重命名和配置指向不同位置的路径以提供更大的灵活性,以进行优化构建。

同样,为避免进行大量配置,最好避免对脚本使用较深的文件夹层次结构,而是将所有脚本都保留在baseUrl中,或者如果要将库/供应商提供的代码与应用程序代码分开,请使用像这样的目录布局:

  • 万维网/
    • index.html
    • js /
      • 应用程序/
        • sub.js
      • lib /
        • jquery.js
        • canvas.js
      • app.js
      • require.js

在index.html中:

<script data-main="js/app.js" src="js/require.js"></script>

并在app.js中:

requirejs.config({
    //By default load any module IDs from js/lib
    baseUrl: 'js/lib',
    //except, if the module ID starts with "app",
    //load it from the js/app directory. paths
    //config is relative to the baseUrl, and
    //never includes a ".js" extension since
    //the paths config could be for a directory.
    paths: {
        app: '../app'
    }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function   ($,        canvas,   sub) {
    //jQuery, canvas and the app/sub module are all
    //loaded and can be used here now.
});

请注意,作为该示例的一部分,像jQuery这样的供应商库在文件名中没有其版本号。如果要跟踪版本信息,建议将其存储在单独的文本文件中,或者如果使用诸如volo之类的工具,它将在package.json中标记版本信息,但将文件保留在磁盘上为“ jquery”。 js”。这使您可以进行非常小的配置,而不必在每个库的“路径”配置中放置一个条目。例如,将“ jquery”配置为“ jquery-1.7.2”。

理想情况下,您加载的脚本将是通过调用define()定义的模块。但是,您可能需要使用某些传统/旧版的“浏览器全局变量”脚本,这些脚本无法通过define()表达它们的依赖性。对于那些,您可以使用shim config。正确表达其依赖性。

如果不表达依赖关系,则由于RequireJS异步加载脚本且速度不佳,可能会导致加载错误。

数据主入口点

data-main属性是一个特殊属性,require.js将检查该属性以开始脚本加载:

<!--when require.js loads it will inject another script tag
    (with async attribute) for scripts/main.js-->
<script data-main="scripts/main" src="scripts/require.js"></script>

通常,您将使用数据主脚本来设置配置选项,然后加载第一个应用程序模块。注意:为您的数据主模块生成的脚本标签require.js包含async属性。这意味着您不能假定数据主脚本的加载和执行将在同一页面稍后引用的其他脚本之前完成。

例如,当在稍后需要require()之前未设置’foo’模块的require.config路径时,这种安排将随机失败:

<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
    paths: {
        foo: 'libs/foo-1.1.3'
    }
});
// contents of other.js:

// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require(['foo'], function(foo) {

});

如果要require()在HTML页面中进行调用,则最好不要使用data-main。data-main仅在页面只有一个主要入口点即data-main脚本时使用。对于要进行内联require()调用的页面,最好将那些页面嵌套require()在配置调用中:

<script src="scripts/require.js"></script>
<script>
require(['scripts/config'], function() {
    // Configuration loaded now, safe to do other require calls
    // that depend on that config.
    require(['foo'], function(foo) {

    });
});
</script>

定义模块

模块与传统脚本文件的不同之处在于,它定义了一个范围广泛的对象,可避免污染全局名称空间。它可以显式列出其依赖关系,并在无需引用全局对象的情况下获取这些依赖关系的句柄,而是将依赖关系作为定义模块的函数的参数来接收。RequireJS中的模块Module Pattern的扩展,其优点是不需要全局变量来引用其他模块。

模块的RequireJS语法允许它们以尽可能快的速度加载,即使顺序混乱也可以,但是以正确的依赖关系顺序进行评估,并且由于未创建全局变量,因此可以在页面中加载模块的多个版本

(如果您熟悉或正在使用CommonJS模块,那么也请参阅CommonJS Notes以获取有关RequireJS模块格式如何映射到CommonJS模块的信息)。

磁盘上每个文件应该只有一个模块定义。可以通过优化工具将模块分组为优化的捆绑包。

简单名称/值对

如果模块没有任何依赖关系,而只是名称/值对的集合,则只需将对象文字传递给define():

//Inside file my/shirt.js:
define({
    color: "black",
    size: "unisize"
});

定义功能

如果模块没有依赖项,但是需要使用函数来完成一些设置工作,则定义自己,然后将函数传递给define():

//my/shirt.js now does setup work
//before returning its module definition.
define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

具有依赖关系的定义函数

如果模块具有依赖项,则第一个参数应为依赖项名称数组,第二个参数应为定义函数。加载所有依赖项后,将调用该函数以定义模块。该函数应返回定义模块的对象。依赖项将作为函数参数传递给定义函数,并以与依赖项数组中的顺序相同的顺序列出:

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

在此示例中,创建了我/衬衫模块。这取决于我/购物车和我/库存。在磁盘上,文件的结构如下:

  • 我/cart.js
  • 我/inventory.js
  • 我的/shirt.js

上面的函数调用指定了两个参数:“购物车”和“库存”。这些是由“ ./cart”和“ ./inventory”模块名称表示的模块。

在加载了my / cart和my / inventory模块之后,该函数才被调用,并且该函数将这些模块作为“ cart”和“ inventory”自变量接收。

明确建议不要使用定义全局变量的模块,以便一次在一个页面中可以存在一个模块的多个版本(请参阅高级用法)。此外,函数参数的顺序应与依赖项的顺序匹配。

函数调用的返回对象定义“我/衬衫”模块。通过以这种方式定义模块,“ my / shirt”就不会作为全局对象存在。

将模块定义为函数

模块不必返回对象。允许来自函数的任何有效返回值。这是一个返回函数作为其模块定义的模块:

//A module definition inside foo/title.js. It uses
//my/cart and my/inventory modules from before,
//but since foo/title.js is in a different directory than
//the "my" modules, it uses the "my" in the module dependency
//name to find them. The "my" part of the name can be mapped
//to any directory, but by default, it is assumed to be a
//sibling to the "foo" directory.
define(["my/cart", "my/inventory"],
    function(cart, inventory) {
        //return a function to define "foo/title".
        //It gets or sets the window title.
        return function(title) {
            return title ? (window.title = title) :
                   inventory.storeName + ' ' + cart.name;
        }
    }
);

使用简化的CommonJS包装器定义模块

果您想重用以传统CommonJS模块格式编写的某些代码,则可能难以对上面使用的依赖项数组进行重新处理,并且您可能更希望将依赖项名称直接与用于此的本地变量对齐依赖性。在以下情况下,可以使用简化的CommonJS包装器

define(function(require, exports, module) {
        var a = require('a'),
            b = require('b');

        //Return the module value
        return function () {};
    }
);

该包装器依靠Function.prototype.toString()给出函数内容的有用字符串值。在某些设备(例如PS3和某些较旧的Opera移动浏览器)上,此功能不起作用。使用优化器以数组格式提取依赖项,以在这些设备上使用。

有关更多信息,请参见CommonJS页面,以及“为什么选择AMD”页面中的“糖”部分

用名称定义模块

您可能会遇到一些define()调用,其中包含模块的名称作为define()的第一个参数:

    //Explicitly defines the "foo/title" module:
    define("foo/title",
        ["my/cart", "my/inventory"],
        function(cart, inventory) {
            //Define foo/title object in here.
       }
    );

这些通常是由优化工具生成的。您可以自己明确命名模块,但这会使模块的可移植性降低-如果将文件移动到另一个目录,则需要更改名称。通常最好避免为模块的名称编码,而让优化工具以模块名称进行刻录。优化工具需要添加名称,以便一个文件中可以捆绑多个模块,以允许在浏览器中更快地加载。

其他模块说明

每个文件一个模块。注意:鉴于模块名称到文件路径查找算法的性质,每个JavaScript文件只能定义一个模块。您仅应使用优化工具将多个模块分组为优化文件。

define()中的相对模块名称:对于在define()函数调用中可能发生的require(“ ../ relative / name”)调用,请务必要求“ require”作为依赖项,以便解析相对名称正确地:

define(["require", "./relative/name"], function(require) {
    var mod = require("./relative/name");
});

更妙的是,使用可用于翻译CommonJS模块的缩短的语法:

define(function(require) {
    var mod = require("./relative/name");
});

这种形式将使用Function.prototype.toString()查找require()调用,并将它们与“ require”一起添加到依赖项数组中,因此代码将在相对路径下正常工作。

如果要在目录中创建一些模块,则相对路径非常有用,这样您就可以与其他人或其他项目共享目录,并且希望能够获得该目录中同级模块的句柄,而不必知道目录的名称。

相对模块名称相对于其他名称,而不是路径:加载程序按模块名称而不是内部路径存储模块。因此,对于相对名称引用,将相对于作为引用的模块名称进行解析,然后,如果需要加载,则将该模块名称或ID转换为路径。其中具有“ main”和“ extras”模块的“ compute”包的示例代码:

* lib/
    * compute/
        * main.js
        * extras.js

main.js模块如下所示:

define(["./extras"], function(extras) {
    //Uses extras in here.
});

如果这是路径配置:

require.config({
    baseUrl: 'lib',
    paths: {
      'compute': 'compute/main'
    }
});

并且require(['compute'])做了,那么LIB /计算/ main.js将有“计算”的模块名称。当它要求“ ./extras”时,相对于“ compute”可以解决,因此“ compute /./ extras”将其标准化为“ extras”。由于没有用于该模块名称的路径配置,因此生成的路径将是针对“ lib / extras.js”的,这是不正确的。

在这种情况下,packages config是一个更好的选择,因为它允许将主模块设置为“计算”,但是在内部加载程序将存储ID为“ compute / main”的模块,以便“ ./演员的作品。

另一个选择是在lib / compute.js处构建一个模块just define(['./compute/main'], function(m) { return m; });,那么就不需要路径或程序包配置了。

或者,不要设置路径或程序包的配置,并且不要在顶层要求调用as require(['compute/main'])

生成相对于模块的URL:您可能需要生成相对于模块的URL。为此,要求“ require”作为依赖项,然后使用require.toUrl()生成URL:

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});

控制台调试:如果需要使用已经通过require(["module/name"], function(){})JavaScript控制台中的调用加载的模块,则可以使用require()形式,该形式仅使用模块的字符串名称来获取它:

require("module/name").callSomeFunction()

请注意,这仅在先前通过require:的异步版本加载“模块/名称”时有效require(["module/name"])。如果使用相对路径,例如“ ./module/name”,则这些仅在内部定义

循环依赖

如果定义循环依赖关系(“ a”需要“ b”,“ b”需要“ a”),则在这种情况下,当调用“ b”的模块函数时,它将获得“ a”的未定义值。在使用require()方法定义模块之后,“ b”可以稍后再获取“ a”(请确保将require指定为依赖项,以便使用正确的上下文来查找“ a”):

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

通常,您不需要使用require()来获取模块,而是依靠将模块作为参数传递给函数。循环依赖关系很少见,通常是您可能需要重新考虑设计的信号。但是,有时需要它们,在这种情况下,请使用上面指定的require()。

如果您熟悉CommonJS模块,则可以使用导出为该模块创建一个空对象,该空对象可立即供其他模块引用。通过在循环依赖关系的两边执行此操作,然后可以安全地保留另一个模块。仅当每个模块都为模块值而不是函数导出对象时,此方法才有效:

//Inside b.js:
define(function(require, exports, module) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.
    var a = require("a");

    exports.foo = function () {
        return a.bar();
    };
});

或者,如果您使用的是依赖项数组方法,则要求特殊的 “ exports”依赖项:

//Inside b.js:
define(['a', 'exports'], function(a, exports) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.

    exports.foo = function () {
        return a.bar();
    };
});

指定JSONP服务依赖关系

JSONP是使用JavaScript调用某些服务的一种方式。它跨域工作,并且是一种仅通过脚本标签仅需要HTTP GET即可调用服务的既定方法。

要在RequireJS中使用JSONP服务,请指定“ define”作为回调参数的值。这意味着您可以获取JSONP URL的值,就好像它是模块定义一样。

这是一个调用JSONP API端点的示例。在此示例中,JSONP回调参数称为“ callback”,因此“ callback = define”告诉API将JSON响应包装在“ define()”包装器中:

require(["http://example.com/api/data.json?callback=define"],
    function (data) {
        //The data object will be the API response for the
        //JSONP data call.
        console.log(data);
    }
);

JSONP的这种使用应限于初始应用程序设置的JSONP服务。如果JSONP服务超时,则意味着可能无法执行通过define()定义的其他模块,因此错误处理不可靠。

仅支持作为JSON对象的JSONP返回值。数组,字符串或数字的JSONP响应将不起作用。

此功能不应用于长轮询JSONP连接-处理实时流的API。这类API在收到每个响应后应该执行更多的脚本清理工作,而RequireJS将只获取一次JSONP URL-随后将相同的URL作为require()或define()调用中的依赖项使用,将获得一个缓存的值。

加载JSONP服务时,错误通常会通过服务超时显示出来,因为脚本标记加载不会为网络问题提供太多详细信息。要检测错误,可以重写requirejs.onError()以获取错误。在“处理错误”部分中有更多信息。

取消定义模块

有一个全局函数requirejs.undef(),它允许取消定义模块。它将重置加载程序的内部状态,以忽略模块的先前定义。

但是,它不会从其他已定义的模块中删除该模块,并且在执行时会将该模块作为依赖项获得该模块的句柄。因此,只有在没有其他模块无法处理该模块值的错误情况下使用它,或者作为将来可能使用该模块的模块加载的一部分,它才真正有用。有关示例,请参见errback部分

如果要进行更复杂的依赖关系图分析以进行未定义的工作,则半私有的 onResourceLoad API可能会有所帮助。

Gulp 项目报错:AssertionError: Task function must be specified

胖蔡阅读(164)

问题

运行gulp项目报错:

> Executing task: npx gulp dev <

[HPM] Proxy created: /services  ->  http://localhost:8081
AssertionError [ERR_ASSERTION]: Task function must be specified
    at Gulp.set [as _setTask] (D:\Projects\web\ptafrontGulp\node_modules\undertaker\lib\set-task.js:10:3)
    at Gulp.task (D:\Projects\web\ptafrontGulp\node_modules\undertaker\lib\task.js:13:8)
    at Object.<anonymous> (D:\Projects\web\ptafrontGulp\gulpfile.js:65:6)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at requireOrImport (D:\Projects\web\ptafrontGulp\node_modules\gulp-cli\lib\shared\require-or-import.js:19:11) {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '=='
}
终端进程“C:\Windows\System32\cmd.exe /d /c npx gulp dev”已终止,退出代码: 1。

解决

gulp项目需要全局安装gulp和项目内安装gulp,通过 gulp -v 查看全局gulp 和本地项目的gulp版本:

D:\Projects\web\ptafrontGulp>gulp -v
CLI version: 2.3.0
Local version: 4.0.2

那么如果CLI 版本和 Local 版本不一致,我们可以修改cli版本或者Local版本来解决这个问题。

让 CLI version 也变成 4.0.2 版本,可执行命令:

$npm install -g gulp@4.0.2

让 Local version 变成 2.3.0 版本,可执行命令:

$yarn add -D  gulp@2.3.0